├── LICENSE ├── README.md ├── docs └── index.html └── src └── jquery.plate.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Krebsz Attila 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jquery-plate # 2 | 3 | An easy to use jQuery plugin for adding nice 3D hover effect on any element. 4 | 5 | Check out the [examples](https://krebszattila.github.io/jquery-plate/)! 6 | 7 | ## Setup ## 8 | 9 | Simply include jquery.plate.js after jQuery. 10 | 11 | ```html 12 | 13 | 14 | ``` 15 | 16 | ## API ## 17 | 18 | ### Initialize ### 19 | 20 | Initialize the plate effect by calling `.plate()` on the selected element(s). 21 | 22 | You can customize the effect by passing an [options](#options) object as an argument. 23 | 24 | ```javascript 25 | $('.plate').plate(); // default options 26 | 27 | $('.plate').plate({ // custom options 28 | inverse: false, 29 | perspective: 500, 30 | maxRotation: 10, 31 | animationDuration: 200 32 | }); 33 | ``` 34 | 35 | ### Reconfigure ### 36 | 37 | Call `.plate(options)` again on an element to modify the settings. 38 | 39 | ```javascript 40 | $('.plate').plate({ // initialize 41 | inverse: false, 42 | animationDuration: 50 43 | }); 44 | 45 | $('.plate').plate({ // reconfigure 46 | inverse: true 47 | }); 48 | ``` 49 | 50 | ### Remove ### 51 | 52 | To remove the effect from an element simply call `remove`. 53 | 54 | ```javascript 55 | $('.plate').plate('remove'); 56 | ``` 57 | 58 | ## Options ## 59 | 60 | * `inverse`: By default the element rotates away from the mouse pointer. Set this option to `true`, if you want to rotate the element towards the mouse pointer. Default value is `false`. 61 | * `perspective`: The transformation [perspective](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/perspective) in pixels. Default is `500`. 62 | * `maxRotation`: The maximum rotation in degrees. Default is `10`. 63 | * `animationDuration`: The animation duration in milliseconds used on mouse enter and mouse leave. Default is `200`. 64 | * `element`: The element which the effect applies to. See details [below](#remote-effect). Default is `undefined`. 65 | 66 | ### Remote effect ### 67 | 68 | If you provide an `element` setting, the plate effect will be applied to that element rather than the selected element. (The mouse event listeners will still be attached to the selected element.) 69 | 70 | Provide a selector string to find an element inside the selected element: 71 | 72 | ```html 73 |
74 |
75 |
76 | ``` 77 | 78 | ```javascript 79 | $('.listener').plate({ 80 | element: '.plate' 81 | }); 82 | ``` 83 | 84 | Or provide a jQuery element to use any element in the document: 85 | 86 | ```html 87 |
88 |
89 | ``` 90 | 91 | ```javascript 92 | $('.listener').plate({ 93 | element: $('.plate') 94 | }); 95 | ``` 96 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | jquery-plate 8 | 9 | 23 | 24 | 25 |

jquery-plate

26 | 27 |

Source and documentation on GitHub

28 | 29 |

Default

30 | 31 |
$('#plate-default').plate();
32 |
#plate-default
33 | 34 |

Inverse

35 |
$('#plate-inverse').plate({
36 |     inverse: true
37 | });
38 |
#plate-inverse
39 | 40 |

Custom

41 |
$('#plate-custom').plate({
42 |     perspective: 200,
43 |     maxRotation: 20,
44 |     animationDuration: 100
45 | });
46 |
#plate-custom
47 | 48 |

Contained

49 |
<div id="listener-contained">
50 |     <div id="plate-contained"></div>
51 | </div>
52 | 
53 |
$('#listener-contained').plate({
54 |     element: '#plate-contained'
55 | });
56 |
57 |
#plate-contained
58 |
59 | 60 |

Remote

61 |
<div id="listener-remote"></div>
62 | <div id="plate-remote"></div>
63 | 
64 |
$('#listener-remote').plate({
65 |     element: $('#plate-remote')
66 | });
67 |
68 |
#plate-remote
69 | 70 | 71 | 72 | 89 | 90 | -------------------------------------------------------------------------------- /src/jquery.plate.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 'use strict'; 3 | 4 | var namespace = 'jquery-plate'; 5 | 6 | function Plate($element, options) { 7 | this.config(options); 8 | 9 | this.$container = $element; 10 | if (this.options.element) { 11 | if (typeof this.options.element === 'string') { 12 | this.$element = this.$container.find(this.options.element); 13 | } else { 14 | this.$element = $(this.options.element); 15 | } 16 | } else { 17 | this.$element = $element; 18 | } 19 | 20 | this.originalTransform = this.$element.css('transform'); 21 | this.$container 22 | .on('mouseenter.' + namespace, this.onMouseEnter.bind(this)) 23 | .on('mouseleave.' + namespace, this.onMouseLeave.bind(this)) 24 | .on('mousemove.' + namespace, this.onMouseMove.bind(this)); 25 | } 26 | 27 | Plate.prototype.config = function(options) { 28 | this.options = $.extend({ 29 | inverse: false, 30 | perspective: 500, 31 | maxRotation: 10, 32 | animationDuration: 200 33 | }, this.options, options); 34 | }; 35 | 36 | Plate.prototype.destroy = function() { 37 | this.$element.css('transform', this.originalTransform); 38 | this.$container.off('.' + namespace); 39 | }; 40 | 41 | Plate.prototype.update = function(offsetX, offsetY, duration) { 42 | var rotateX; 43 | var rotateY; 44 | 45 | if (offsetX || offsetX === 0) { 46 | var height = this.$container.outerHeight(); 47 | var py = (offsetY - height / 2) / (height / 2); 48 | rotateX = this.round(this.options.maxRotation * -py); 49 | } else { 50 | rotateY = 0; 51 | } 52 | 53 | if (offsetY || offsetY === 0) { 54 | var width = this.$container.outerWidth(); 55 | var px = (offsetX - width / 2) / (width / 2); 56 | rotateY = this.round(this.options.maxRotation * px); 57 | } else { 58 | rotateX = 0; 59 | } 60 | 61 | if (this.options.inverse) { 62 | rotateX *= -1; 63 | rotateY *= -1; 64 | } 65 | 66 | if (duration) { 67 | this.animate(rotateX, rotateY, duration); 68 | } else if (this.animation && this.animation.remaining) { 69 | this.animation.targetX = rotateX; 70 | this.animation.targetY = rotateY; 71 | } else { 72 | this.transform(rotateX, rotateY); 73 | } 74 | }; 75 | 76 | Plate.prototype.reset = function(duration) { 77 | this.update(null, null, duration); 78 | }; 79 | 80 | Plate.prototype.transform = function(rotateX, rotateY) { 81 | this.currentX = rotateX; 82 | this.currentY = rotateY; 83 | this.$element.css('transform', 84 | (this.originalTransform && this.originalTransform !== 'none' ? this.originalTransform + ' ' : '') + 85 | 'perspective(' + this.options.perspective + 'px) ' + 86 | 'rotateX(' + rotateX + 'deg) rotateY(' + rotateY + 'deg)' 87 | ); 88 | }; 89 | 90 | Plate.prototype.animate = function(rotateX, rotateY, duration) { 91 | if (duration) { 92 | this.animation = this.animation || {}; 93 | 94 | var remaining = this.animation.remaining; 95 | this.animation.time = performance.now(); 96 | this.animation.remaining = duration || null; 97 | this.animation.targetX = rotateX; 98 | this.animation.targetY = rotateY; 99 | 100 | if (!remaining) { 101 | requestAnimationFrame(this.onAnimationFrame.bind(this)); 102 | } 103 | } else { 104 | this.transform(rotateX, rotateY); 105 | } 106 | }; 107 | 108 | Plate.prototype.round = function(number) { 109 | return Math.round(number * 1000) / 1000; 110 | }; 111 | 112 | Plate.prototype.offsetCoords = function(event) { 113 | var offset = this.$container.offset(); 114 | return { 115 | x: event.pageX - offset.left, 116 | y: event.pageY - offset.top 117 | }; 118 | }; 119 | 120 | Plate.prototype.onAnimationFrame = function(timestamp) { 121 | this.animation = this.animation || {}; 122 | 123 | var delta = timestamp - (this.animation.time || 0); 124 | this.animation.time = timestamp; 125 | 126 | var duration = this.animation.remaining || 0; 127 | var percent = Math.min(delta / duration, 1); 128 | var currentX = this.currentX || 0; 129 | var currentY = this.currentY || 0; 130 | var targetX = this.animation.targetX || 0; 131 | var targetY = this.animation.targetY || 0; 132 | var rotateX = this.round(currentX + (targetX - currentX) * percent); 133 | var rotateY = this.round(currentY + (targetY - currentY) * percent); 134 | this.transform(rotateX, rotateY); 135 | 136 | var remaining = duration - delta; 137 | this.animation.remaining = remaining > 0 ? remaining : null; 138 | if (remaining > 0) { 139 | requestAnimationFrame(this.onAnimationFrame.bind(this)); 140 | } 141 | }; 142 | 143 | Plate.prototype.onMouseEnter = function(event) { 144 | var offset = this.offsetCoords(event); 145 | this.update(offset.x, offset.y, this.options.animationDuration); 146 | }; 147 | 148 | Plate.prototype.onMouseLeave = function(event) { 149 | this.reset(this.options.animationDuration); 150 | }; 151 | 152 | Plate.prototype.onMouseMove = function(event) { 153 | var offset = this.offsetCoords(event); 154 | this.update(offset.x, offset.y); 155 | }; 156 | 157 | $.fn.plate = function(options) { 158 | return this.each(function() { 159 | var $element = $(this); 160 | var plate = $element.data(namespace); 161 | 162 | if (options === 'remove') { 163 | plate.destroy(); 164 | $element.data(namespace, null); 165 | } else { 166 | if (!plate) { 167 | plate = new Plate($element, options); 168 | $element.data(namespace, plate); 169 | plate.reset(); 170 | } else { 171 | plate.config(options); 172 | } 173 | } 174 | }); 175 | }; 176 | 177 | })(jQuery); 178 | --------------------------------------------------------------------------------