├── 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 |
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 |
--------------------------------------------------------------------------------