├── .babelrc
├── logo.png
├── index.js
├── package.json
├── LICENSE
├── .gitignore
├── README.md
└── dist.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["env", "react"]
3 | }
4 |
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siddharthkp/react-vanilla-tilt/HEAD/logo.png
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import VanillaTilt from 'vanilla-tilt'
3 |
4 | class Tilt extends React.Component {
5 | constructor(props) {
6 | super(props)
7 | this.tiltRef = React.createRef()
8 | }
9 |
10 | componentDidMount() {
11 | VanillaTilt.init(this.tiltRef.current, this.props.options)
12 | }
13 |
14 | render() {
15 | return
16 | }
17 | }
18 |
19 | export default Tilt
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-vanilla-tilt",
3 | "version": "1.0.0",
4 | "description": "React wrapper around vanilla-tilt",
5 | "main": "dist.js",
6 | "scripts": {
7 | "build": "babel index.js -o dist.js"
8 | },
9 | "files": ["dist.js"],
10 | "keywords": ["react", "vanilla", "tilt"],
11 | "author": "siddharthkp",
12 | "license": "MIT",
13 | "dependencies": {
14 | "vanilla-tilt": "1.4.1"
15 | },
16 | "devDependencies": {
17 | "babel-cli": "6.26.0",
18 | "babel-preset-env": "1.6.1",
19 | "babel-preset-react": "6.24.1"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Siddharth Kshetrapal
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 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # next.js build output
61 | .next
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | A smooth 3D tilt React component wrapped around vanilla-tilt.js
5 |
6 |
7 |
8 |
9 |
10 | #### install
11 |
12 | ```
13 | npm install react-vanilla-tilt
14 | ```
15 |
16 |
17 |
18 | #### usage
19 |
20 | ```jsx
21 | import React from 'react'
22 | import Tilt from 'react-vanilla-tilt'
23 |
24 |
25 |
26 | You can put whatever you want inside this
27 |
28 |
29 | ```
30 |
31 |
32 |
33 | #### options
34 |
35 | ```js
36 | {
37 | reverse: false, // reverse the tilt direction
38 | max: 35, // max tilt rotation (degrees)
39 | perspective: 1000, // Transform perspective, the lower the more extreme the tilt gets.
40 | scale: 1, // 2 = 200%, 1.5 = 150%, etc..
41 | speed: 300, // Speed of the enter/exit transition
42 | transition: true, // Set a transition on enter/exit.
43 | axis: null, // What axis should be disabled. Can be X or Y.
44 | reset: true, // If the tilt effect has to be reset on exit.
45 | easing: "cubic-bezier(.03,.98,.52,.99)", // Easing on enter/exit.
46 | glare: false, // if it should have a "glare" effect
47 | "max-glare": 1, // the maximum "glare" opacity (1 = 100%, 0.5 = 50%)
48 | "glare-prerender": false // false = VanillaTilt creates the glare elements for you, otherwise
49 | // you need to add .js-tilt-glare>.js-tilt-glare-inner by yourself
50 | }
51 | ```
52 |
53 | ```jsx
54 | import React from 'react'
55 | import Title from 'react-vanilla-tilt'
56 |
57 |
58 |
59 | You can put whatever you want inside this
60 |
61 |
62 | ```
63 |
64 |
65 |
66 | #### Related projects
67 |
68 | - [card-vibes](https://github.com/siddharthkp/card-vibes): The card component with good vibes built on top of this project
69 |
70 | Thanks to
71 | - [vanilla-tilt.js](https://github.com/micku7zu/vanilla-tilt.js) by [@micku7zu](https://github.com/micku7zu)
72 | - [tilt.js](https://github.com/gijsroge/tilt.js) by [@gijsroge](https://github.com/gijsroge)
73 |
74 |
75 |
76 | #### like it?
77 |
78 | :star: this repo
79 |
80 |
81 |
82 | #### license
83 |
84 | MIT © [siddharthkp](https://github.com/siddharthkp)
85 |
--------------------------------------------------------------------------------
/dist.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
8 |
9 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
10 |
11 | var _react = require('react');
12 |
13 | var _react2 = _interopRequireDefault(_react);
14 |
15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16 |
17 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
18 |
19 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
20 |
21 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
22 |
23 | function VanillaTilt(element) {
24 | this.width = null;
25 | this.height = null;
26 | this.left = null;
27 | this.top = null;
28 | this.transitionTimeout = null;
29 | this.updateCall = null;
30 |
31 | this.updateBind = this.update.bind(this);
32 | this.resetBind = this.reset.bind(this);
33 |
34 | this.element = element;
35 |
36 | this.settings = this.getSettings();
37 |
38 | this.element.style.boxShadow = this.getShadow(this.settings);
39 |
40 | this.reverse = this.settings.reverse ? -1 : 1;
41 |
42 | this.addEventListeners();
43 | }
44 |
45 | VanillaTilt.prototype.getShadow = function getShadow(settings, values) {
46 | if (!values) values = { percentageX: 50, percentageY: 50 };
47 | var shadow = this.settings.shadow;
48 |
49 | return shadow.color + ' ' + (shadow.x.min + 0.01 * values.percentageX * (shadow.x.max - shadow.x.min)) + 'px ' + (shadow.y.min + 0.01 * values.percentageY * (shadow.y.max - shadow.y.min)) + 'px ' + shadow.spread + 'px';
50 | };
51 |
52 | VanillaTilt.prototype.isSettingTrue = function isSettingTrue(setting) {
53 | return setting === '' || setting === true || setting === 1;
54 | };
55 |
56 | VanillaTilt.prototype.addEventListeners = function addEventListeners() {
57 | this.onMouseEnterBind = this.onMouseEnter.bind(this);
58 | this.onMouseMoveBind = this.onMouseMove.bind(this);
59 | this.onMouseLeaveBind = this.onMouseLeave.bind(this);
60 |
61 | this.element.addEventListener('mouseenter', this.onMouseEnterBind);
62 | this.element.addEventListener('mousemove', this.onMouseMoveBind);
63 | this.element.addEventListener('mouseleave', this.onMouseLeaveBind);
64 | };
65 |
66 | VanillaTilt.prototype.removeEventListeners = function removeEventListeners() {
67 | this.element.removeEventListener('mouseenter', this.onMouseEnterBind);
68 | this.element.removeEventListener('mousemove', this.onMouseMoveBind);
69 | this.element.removeEventListener('mouseleave', this.onMouseLeaveBind);
70 | };
71 |
72 | VanillaTilt.prototype.destroy = function destroy() {
73 | clearTimeout(this.transitionTimeout);
74 | if (this.updateCall !== null) cancelAnimationFrame(this.updateCall);
75 |
76 | this.reset();
77 |
78 | this.removeEventListeners();
79 | this.element.vanillaTilt = null;
80 | delete this.element.vanillaTilt;
81 |
82 | this.element = null;
83 | };
84 |
85 | VanillaTilt.prototype.onMouseEnter = function onMouseEnter(event) {
86 | this.updateElementPosition();
87 | this.element.style.willChange = 'transform';
88 | this.setTransition();
89 | };
90 |
91 | VanillaTilt.prototype.onMouseMove = function onMouseMove(event) {
92 | if (this.updateCall !== null) {
93 | cancelAnimationFrame(this.updateCall);
94 | }
95 |
96 | this.event = event;
97 | this.updateCall = requestAnimationFrame(this.updateBind);
98 | };
99 |
100 | VanillaTilt.prototype.onMouseLeave = function onMouseLeave(event) {
101 | this.setTransition();
102 | requestAnimationFrame(this.resetBind);
103 | };
104 |
105 | VanillaTilt.prototype.reset = function reset() {
106 | this.event = {
107 | pageX: this.left + this.width / 2,
108 | pageY: this.top + this.height / 2
109 | };
110 |
111 | this.element.style.transform = 'perspective(' + this.settings.perspective + 'px) ' + 'rotateX(0deg) ' + 'rotateY(0deg) ' + 'scale3d(1, 1, 1)';
112 |
113 | this.element.style.boxShadow = this.getShadow(this.settings);
114 | };
115 |
116 | VanillaTilt.prototype.getValues = function getValues() {
117 | var x = (this.event.clientX - this.left) / this.width;
118 | var y = (this.event.clientY - this.top) / this.height;
119 |
120 | x = Math.min(Math.max(x, 0), 1);
121 | y = Math.min(Math.max(y, 0), 1);
122 |
123 | var tiltX = (this.reverse * (this.settings.max / 2 - x * this.settings.max)).toFixed(2);
124 | var tiltY = (this.reverse * (y * this.settings.max - this.settings.max / 2)).toFixed(2);
125 | var angle = Math.atan2(this.event.clientX - (this.left + this.width / 2), -(this.event.clientY - (this.top + this.height / 2))) * (180 / Math.PI);
126 |
127 | return {
128 | tiltX: tiltX,
129 | tiltY: tiltY,
130 | percentageX: x * 100,
131 | percentageY: y * 100,
132 | angle: angle
133 | };
134 | };
135 |
136 | VanillaTilt.prototype.updateElementPosition = function updateElementPosition() {
137 | var rect = this.element.getBoundingClientRect();
138 |
139 | this.width = this.element.offsetWidth;
140 | this.height = this.element.offsetHeight;
141 | this.left = rect.left;
142 | this.top = rect.top;
143 | };
144 |
145 | VanillaTilt.prototype.update = function update() {
146 | var values = this.getValues();
147 |
148 | this.element.style.transform = 'perspective(' + this.settings.perspective + 'px) ' + 'rotateX(' + (this.settings.axis === 'x' ? 0 : values.tiltY) + 'deg) ' + 'rotateY(' + (this.settings.axis === 'y' ? 0 : values.tiltX) + 'deg) ' + 'scale3d(' + this.settings.scale + ', ' + this.settings.scale + ', ' + this.settings.scale + ')';
149 |
150 | this.element.style.boxShadow = this.getShadow(this.settings, values);
151 |
152 | this.element.dispatchEvent(new CustomEvent('tiltChange', { detail: values }));
153 |
154 | this.updateCall = null;
155 | };
156 |
157 | VanillaTilt.prototype.setTransition = function setTransition() {
158 | var _this = this;
159 |
160 | clearTimeout(this.transitionTimeout);
161 | this.element.style.transition = this.settings.speed + 'ms ' + this.settings.easing;
162 |
163 | this.transitionTimeout = setTimeout(function () {
164 | _this.element.style.transition = '';
165 | }, this.settings.speed);
166 | };
167 |
168 | VanillaTilt.prototype.getSettings = function getSettings() {
169 | var defaultSettings = {
170 | reverse: true,
171 | max: 15,
172 | perspective: 1000,
173 | easing: 'cubic-bezier(.03,.98,.52,.99)',
174 | scale: '1',
175 | speed: '300',
176 | transition: true,
177 | axis: null,
178 | reset: true,
179 | shadow: {
180 | color: 'rgba(20, 26, 40, 0.2)',
181 | x: { min: -5, max: 5 },
182 | y: { min: 3.5, max: 10.5 },
183 | spread: 42
184 | }
185 | };
186 |
187 | return defaultSettings;
188 | };
189 |
190 | VanillaTilt.init = function init(elements, settings) {
191 | if (elements instanceof Node) {
192 | elements = [elements];
193 | }
194 |
195 | if (elements instanceof NodeList) {
196 | elements = [].slice.call(elements);
197 | }
198 |
199 | if (!(elements instanceof Array)) {
200 | return;
201 | }
202 |
203 | elements.forEach(function (element) {
204 | if (!('vanillaTilt' in element)) {
205 | element.vanillaTilt = new VanillaTilt(element, settings);
206 | }
207 | });
208 | };
209 |
210 | var cardStyles = {
211 | width: '300px',
212 | padding: '30px',
213 | margin: '10px',
214 | background: '#fff',
215 | borderRadius: '4px',
216 | color: '#364962',
217 | fontSize: '16px',
218 | lineHeight: 1.6
219 | };
220 |
221 | var Card = function (_React$Component) {
222 | _inherits(Card, _React$Component);
223 |
224 | function Card() {
225 | _classCallCheck(this, Card);
226 |
227 | return _possibleConstructorReturn(this, (Card.__proto__ || Object.getPrototypeOf(Card)).apply(this, arguments));
228 | }
229 |
230 | _createClass(Card, [{
231 | key: 'componentDidMount',
232 | value: function componentDidMount() {
233 | VanillaTilt.init(document.querySelectorAll('[data-tilt]'));
234 | }
235 | }, {
236 | key: 'render',
237 | value: function render() {
238 | var styles = Object.assign({}, cardStyles, this.props.style);
239 | return _react2.default.createElement('div', _extends({ 'data-tilt': true, style: styles }, this.props));
240 | }
241 | }]);
242 |
243 | return Card;
244 | }(_react2.default.Component);
245 |
246 | exports.default = Card;
247 |
--------------------------------------------------------------------------------