├── .DS_Store
├── .gitattributes
├── README.md
├── admin
├── .DS_Store
├── base.gif
└── base.png
├── css
└── base.css
├── img
├── .DS_Store
└── 1.jpg
├── index.html
└── js
└── index.js
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/python019/motion-effect-svg-filter-gsap/1ac4c4c3c19c5e18f84f43b7a0de6d327c6f6ede/.DS_Store
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.md linguist-language=scss
2 | *.html linguist-language=js
3 | *.css linguist-language=js
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Image Motion Effect with SVG Filter Gsap | Crimson
4 |
5 |

6 |
7 | ### by
SUBUX
8 |
9 |
--------------------------------------------------------------------------------
/admin/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/python019/motion-effect-svg-filter-gsap/1ac4c4c3c19c5e18f84f43b7a0de6d327c6f6ede/admin/.DS_Store
--------------------------------------------------------------------------------
/admin/base.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/python019/motion-effect-svg-filter-gsap/1ac4c4c3c19c5e18f84f43b7a0de6d327c6f6ede/admin/base.gif
--------------------------------------------------------------------------------
/admin/base.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/python019/motion-effect-svg-filter-gsap/1ac4c4c3c19c5e18f84f43b7a0de6d327c6f6ede/admin/base.png
--------------------------------------------------------------------------------
/css/base.css:
--------------------------------------------------------------------------------
1 | *,
2 | *::after,
3 | *::before {
4 | box-sizing: border-box;
5 | }
6 |
7 | :root {
8 | font-size: 15px;
9 | }
10 |
11 | body {
12 | margin: 0;
13 | --color-text: #fff;
14 | --color-bg: #0d0d0e;
15 | --color-link: #b3382c;
16 | --color-link-hover: #d6665b;
17 | color: var(--color-text);
18 | background: var(--color-bg);
19 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif;
20 | font-weight: 500;
21 | -webkit-font-smoothing: antialiased;
22 | -moz-osx-font-smoothing: grayscale;
23 | width: 100%;
24 | height: 100vh;
25 | overflow: hidden;
26 | }
27 |
28 | a {
29 | text-decoration: none;
30 | color: var(--color-link);
31 | outline: none;
32 | }
33 |
34 | a:hover {
35 | color: var(--color-link-hover);
36 | outline: none;
37 | }
38 |
39 | a:focus {
40 | outline: none;
41 | background: lightgrey;
42 | }
43 |
44 | a:focus:not(:focus-visible) {
45 | background: transparent;
46 | }
47 |
48 | a:focus-visible {
49 | outline: 2px solid red;
50 | background: transparent;
51 | }
52 |
53 | .frame {
54 | padding: 1.5rem 2rem 10vh;
55 | text-align: center;
56 | position: relative;
57 | z-index: 100;
58 | }
59 |
60 | .frame__title {
61 | margin: 0;
62 | font-size: 1rem;
63 | font-weight: 500;
64 | }
65 |
66 | .frame__links {
67 | margin: 0.5rem 0 2rem;
68 | }
69 |
70 | .frame__links a:not(:last-child) {
71 | margin-right: 1rem;
72 | }
73 |
74 | .content {
75 | flex: 1;
76 | display: grid;
77 | place-items: center;
78 | position: absolute;
79 | width: 100%;
80 | height: 100%;
81 | }
82 |
83 | #theSVG {
84 | position: relative;
85 | display: grid;
86 | place-items: center;
87 | max-height: 65vh;
88 | grid-area: 1 / 1 / 2 / 2;
89 | will-change: transform;
90 | }
91 |
92 | @media screen and (min-width: 53em) {
93 | main {
94 | height: 100vh;
95 | display: flex;
96 | flex-direction: column;
97 | }
98 | .frame {
99 | padding: 1.5rem 2rem 0;
100 | display: grid;
101 | grid-template-columns: auto 1fr auto;
102 | grid-template-areas: 'title links sponsor';
103 | grid-gap: 3vw;
104 | justify-content: space-between;
105 | text-align: left;
106 | }
107 | .frame__links {
108 | margin: 0;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/img/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/python019/motion-effect-svg-filter-gsap/1ac4c4c3c19c5e18f84f43b7a0de6d327c6f6ede/img/.DS_Store
--------------------------------------------------------------------------------
/img/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/python019/motion-effect-svg-filter-gsap/1ac4c4c3c19c5e18f84f43b7a0de6d327c6f6ede/img/1.jpg
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Image Motion Effect with SVG Filter Gsap| Crimson
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/js/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Linear interpolation
3 | * @param {Number} a - first value to interpolate
4 | * @param {Number} b - second value to interpolate
5 | * @param {Number} n - amount to interpolate
6 | */
7 | const lerp = (a, b, n) => (1 - n) * a + n * b;
8 |
9 | /**
10 | * Gets the cursor position
11 | * @param {Event} ev - mousemove event
12 | */
13 | const getCursorPos = ev => {
14 | return {
15 | x : ev.clientX,
16 | y : ev.clientY
17 | };
18 | };
19 |
20 | /**
21 | * Map number x from range [a, b] to [c, d]
22 | * @param {Number} x - changing value
23 | * @param {Number} a
24 | * @param {Number} b
25 | * @param {Number} c
26 | * @param {Number} d
27 | */
28 | const map = (x, a, b, c, d) => (x - a) * (d - c) / (b - a) + c;
29 |
30 | /**
31 | * Distance between point A(x1,y1) and B(x2,y2)
32 | * @param {Number} x1
33 | * @param {Number} x2
34 | * @param {Number} y1
35 | * @param {Number} y2
36 | */
37 | const distance = (x1,x2,y1,y2) => {
38 | var a = x1 - x2;
39 | var b = y1 - y2;
40 | return Math.hypot(a,b);
41 | };
42 |
43 | /**
44 | * Calculates the viewport size
45 | */
46 | const calcWinsize = () => {
47 | return {
48 | width: window.innerWidth,
49 | height: window.innerHeight
50 | }
51 | }
52 |
53 | // Viewport size
54 | let winsize = calcWinsize();
55 | // Re-calculate on resize
56 | window.addEventListener('resize', () => winsize = calcWinsize());
57 |
58 | // Track the cursor position
59 | let cursor = {x: winsize.width/2, y: winsize.height/2};
60 | let cachedCursor = cursor;
61 | window.addEventListener('mousemove', ev => cursor = getCursorPos(ev));
62 |
63 | /**
64 | * Class representing an SVG image that follows the cursor
65 | * and gets "distorted" as the cursor moves faster.
66 | */
67 | class SVGImageFilterEffect {
68 | // DOM elements
69 | DOM = {
70 | // Main element (SVG element)
71 | el: null,
72 | // the SVG image
73 | image: null,
74 | // feDisplacementMap element
75 | feDisplacementMapEl: null,
76 | };
77 | // option defaults
78 | defaults = {
79 | // How much to translate and rotate the image
80 | // Also the range of the displacementScale values
81 | valuesFromTo: {
82 | transform: {
83 | x: [-120,120],
84 | y: [-120,120],
85 | rz: [-10,10]
86 | },
87 | displacementScale: [0, 400],
88 | },
89 | // The "amt" is the amount to interpolate.
90 | // With interpolation, we can achieve a smooth animation effect when moving the cursor.
91 | amt: {
92 | transform: 0.1,
93 | displacementScale: 0.06,
94 | },
95 | };
96 | // Values that change when moving the cursor (transform and feDisplacementMap scale values)
97 | imgValues = {
98 | imgTransforms: {x: 0, y: 0, rz: 0},
99 | displacementScale: 0,
100 | };
101 |
102 | /**
103 | * Constructor.
104 | * @param {Element} DOM_el - the SVG element
105 | * @param {JSON} options
106 | */
107 | constructor(DOM_el, options) {
108 | this.DOM.el = DOM_el;
109 | this.DOM.image = this.DOM.el.querySelector('image');
110 | this.DOM.feDisplacementMapEl = this.DOM.el.querySelector('feDisplacementMap');
111 |
112 | this.options = Object.assign(this.defaults, options);
113 |
114 | requestAnimationFrame(() => this.render());
115 | }
116 | /**
117 | * Loop / Interpolation
118 | */
119 | render() {
120 | // Apply interpolated values (smooth effect)
121 | this.imgValues.imgTransforms.x = lerp(this.imgValues.imgTransforms.x, map(cursor.x, 0, winsize.width, this.options.valuesFromTo.transform.x[0], this.options.valuesFromTo.transform.x[1]), this.options.amt.transform);
122 | this.imgValues.imgTransforms.y = lerp(this.imgValues.imgTransforms.y, map(cursor.y, 0, winsize.height, this.options.valuesFromTo.transform.y[0], this.options.valuesFromTo.transform.y[1]), this.options.amt.transform);
123 | this.imgValues.imgTransforms.rz = lerp(this.imgValues.imgTransforms.rz, map(cursor.x, 0, winsize.width, this.options.valuesFromTo.transform.rz[0], this.options.valuesFromTo.transform.rz[1]), this.options.amt.transform);
124 |
125 | this.DOM.el.style.transform = `translateX(${(this.imgValues.imgTransforms.x)}px) translateY(${this.imgValues.imgTransforms.y}px) rotateZ(${this.imgValues.imgTransforms.rz}deg)`;
126 |
127 | const cursorTravelledDistance = distance(cachedCursor.x, cursor.x, cachedCursor.y, cursor.y);
128 | this.imgValues.displacementScale = lerp(this.imgValues.displacementScale, map(cursorTravelledDistance, 0, 200, this.options.valuesFromTo.displacementScale[0], this.options.valuesFromTo.displacementScale[1]), this.options.amt.displacementScale);
129 | this.DOM.feDisplacementMapEl.scale.baseVal = this.imgValues.displacementScale;
130 |
131 | cachedCursor = cursor;
132 |
133 | // loop...
134 | requestAnimationFrame(() => this.render());
135 | }
136 | }
137 |
138 | // Initialize trail effect
139 | new SVGImageFilterEffect(document.querySelector('#theSVG'));
--------------------------------------------------------------------------------