11/* eslint-disable no-restricted-globals */
22const $ = ( el , sel ) => el . querySelector ( sel ) ;
33const $$ = ( el , sel ) => el . querySelectorAll ( sel ) ;
4- const $setDuration = ( el , duration ) => {
5- el . style . transitionDuration = duration ;
6- } ;
7- const $setTransform = ( el , transform ) => {
8- el . style . transform = transform ;
9- } ;
10- const $setOpacity = ( el , opacity ) => {
11- el . style . opacity = opacity ;
12- } ;
13- const $on = ( el , event , handler , props ) => el . addEventListener ( event , handler , props ) ;
14- const $off = ( el , event , handler , props ) => el . removeEventListener ( event , handler , props ) ;
154
165const removeUndefinedProps = ( obj = { } ) => {
176 const result = { } ;
@@ -30,9 +19,7 @@ function Atropos(originalParams = {}) {
3019 activeOffset : 50 ,
3120 shadowOffset : 50 ,
3221 shadowScale : 1 ,
33- durationEnter : 300 ,
34- durationLeave : 600 ,
35- rotateLock : true ,
22+ duration : 300 ,
3623 rotate : true ,
3724 rotateTouch : true ,
3825 rotateXMax : 15 ,
@@ -55,22 +42,53 @@ function Atropos(originalParams = {}) {
5542 const { params } = self ;
5643
5744 let rotateEl ;
58- let rotated ;
5945 let scaleEl ;
6046 let innerEl ;
6147
62- let enterRotateX ;
63- let enterRotateY ;
64-
6548 let elBoundingClientRect ;
6649 let eventsElBoundingClientRect ;
6750
68- let rotateXLock = true ;
69- let rotateYLock = true ;
70-
7151 let shadowEl ;
7252 let highlightEl ;
7353
54+ let isScrolling ;
55+ let clientXStart ;
56+ let clientYStart ;
57+
58+ const queue = [ ] ;
59+ let queueFrameId ;
60+ const purgeQueue = ( ) => {
61+ queueFrameId = requestAnimationFrame ( ( ) => {
62+ queue . forEach ( ( data ) => {
63+ if ( typeof data === 'function' ) {
64+ data ( ) ;
65+ } else {
66+ const { element, prop, value } = data ;
67+ element . style [ prop ] = value ;
68+ }
69+ } ) ;
70+ queue . splice ( 0 , queue . length ) ;
71+ purgeQueue ( ) ;
72+ } ) ;
73+ } ;
74+ purgeQueue ( ) ;
75+
76+ const $setDuration = ( element , value ) => {
77+ queue . push ( { element, prop : 'transitionDuration' , value } ) ;
78+ } ;
79+ const $setEasing = ( element , value ) => {
80+ queue . push ( { element, prop : 'transitionTimingFunction' , value } ) ;
81+ } ;
82+ const $setTransform = ( element , value ) => {
83+ queue . push ( { element, prop : 'transform' , value } ) ;
84+ } ;
85+ const $setOpacity = ( element , value ) => {
86+ queue . push ( { element, prop : 'opacity' , value } ) ;
87+ } ;
88+ const $on = ( element , event , handler , props ) => element . addEventListener ( event , handler , props ) ;
89+ const $off = ( element , event , handler , props ) =>
90+ element . removeEventListener ( event , handler , props ) ;
91+
7492 const createShadow = ( ) => {
7593 let created ;
7694 shadowEl = $ ( el , '.atropos-shadow' ) ;
@@ -107,6 +125,7 @@ function Atropos(originalParams = {}) {
107125 rotateYPercentage = 0 ,
108126 duration,
109127 opacityOnly,
128+ easeOut,
110129 } ) => {
111130 const getOpacity = ( element ) => {
112131 if ( element . dataset . atroposOpacity && typeof element . dataset . atroposOpacity === 'string' ) {
@@ -116,6 +135,7 @@ function Atropos(originalParams = {}) {
116135 } ;
117136 $$ ( el , '[data-atropos-offset], [data-atropos-opacity]' ) . forEach ( ( childEl ) => {
118137 $setDuration ( childEl , duration ) ;
138+ $setEasing ( childEl , easeOut ? 'ease-out' : '' ) ;
119139 const elementOpacity = getOpacity ( childEl ) ;
120140 if ( rotateXPercentage === 0 && rotateYPercentage === 0 ) {
121141 if ( ! opacityOnly ) $setTransform ( childEl , `translate3d(0, 0, 0)` ) ;
@@ -143,30 +163,31 @@ function Atropos(originalParams = {}) {
143163 } ;
144164
145165 const onPointerEnter = ( e ) => {
166+ isScrolling = undefined ;
146167 if ( e . type === 'pointerdown' && e . pointerType === 'mouse' ) return ;
147168 if ( e . type === 'pointerenter' && e . pointerType !== 'mouse' ) return ;
148169 if ( e . type === 'pointerdown' ) {
149170 e . preventDefault ( ) ;
150171 }
151- el . classList . add ( 'atropos-active' ) ;
152- $setDuration ( rotateEl , '0ms' ) ;
153- rotated = false ;
154- enterRotateX = undefined ;
155- enterRotateY = undefined ;
156- rotateXLock = true ;
157- rotateYLock = true ;
172+ clientXStart = e . clientX ;
173+ clientYStart = e . clientY ;
174+ queue . push ( ( ) => el . classList . add ( 'atropos-active' ) ) ;
175+ $setDuration ( rotateEl , `${ params . duration } ms` ) ;
176+ $setEasing ( rotateEl , 'ease-out' ) ;
158177 $setTransform ( scaleEl , `translate3d(0,0, ${ params . activeOffset } px)` ) ;
159- $setDuration ( scaleEl , `${ params . rotateLock ? params . durationEnter : 0 } ms` ) ;
178+ $setDuration ( scaleEl , `${ params . duration } ms` ) ;
179+ $setEasing ( scaleEl , 'ease-out' ) ;
160180 if ( shadowEl ) {
161- $setDuration ( shadowEl , `${ params . rotateLock ? params . durationEnter : 0 } ms` ) ;
181+ $setDuration ( shadowEl , `${ params . duration } ms` ) ;
182+ $setEasing ( shadowEl , 'ease-out' ) ;
162183 }
163184
164185 self . isActive = true ;
165186 if ( typeof params . onEnter === 'function' ) params . onEnter ( ) ;
166187 } ;
167188
168189 const onTouchMove = ( e ) => {
169- if ( rotated && e . cancelable ) {
190+ if ( isScrolling === false && e . cancelable ) {
170191 e . preventDefault ( ) ;
171192 }
172193 } ;
@@ -216,51 +237,32 @@ function Atropos(originalParams = {}) {
216237 rotateX = ( params . rotateXMax * ( coordY - centerY ) ) / ( parentHeight - height / 2 ) ;
217238 }
218239
219- if ( params . rotateLock ) {
220- if ( typeof enterRotateY === 'undefined' ) {
221- enterRotateY = rotateY ;
222- rotateYLock = true ;
223- }
224- if ( typeof enterRotateX === 'undefined' ) {
225- enterRotateX = rotateX ;
226- rotateXLock = true ;
227- }
228- if ( rotateYLock ) {
229- if ( enterRotateY < 0 ) {
230- if ( rotateY < 0 ) rotateY = 0 ;
231- if ( rotateY > 0 ) rotateYLock = false ;
232- }
233- if ( enterRotateY > 0 ) {
234- if ( rotateY > 0 ) rotateY = 0 ;
235- if ( rotateY < 0 ) rotateYLock = false ;
236- }
237- }
238- if ( rotateXLock ) {
239- if ( enterRotateX < 0 ) {
240- if ( rotateX < 0 ) rotateX = 0 ;
241- if ( rotateX > 0 ) rotateXLock = false ;
242- }
243- if ( enterRotateX > 0 ) {
244- if ( rotateX > 0 ) rotateX = 0 ;
245- if ( rotateX < 0 ) rotateXLock = false ;
246- }
247- }
248- }
249-
250240 rotateX = Math . min ( Math . max ( - rotateX , - params . rotateXMax ) , params . rotateXMax ) ;
251241 if ( params . rotateXInvert ) rotateX = - rotateX ;
252242 rotateY = Math . min ( Math . max ( - rotateY , - params . rotateYMax ) , params . rotateYMax ) ;
253243 if ( params . rotateYInvert ) rotateY = - rotateY ;
254244
255- if ( typeof params . rotateTouch === 'string' && ( rotateX !== 0 || rotateY !== 0 ) ) {
256- if ( ! rotated ) {
257- rotated = true ;
258- el . classList . add ( 'atropos-rotate-touch' ) ;
245+ if (
246+ typeof params . rotateTouch === 'string' &&
247+ ( rotateX !== 0 || rotateY !== 0 ) &&
248+ typeof isScrolling === 'undefined'
249+ ) {
250+ const diffX = clientX - clientXStart ;
251+ const diffY = clientY - clientYStart ;
252+ if ( diffX * diffX + diffY * diffY >= 25 ) {
253+ const touchAngle = ( Math . atan2 ( Math . abs ( diffY ) , Math . abs ( diffX ) ) * 180 ) / Math . PI ;
254+ isScrolling = params . rotateTouch === 'scroll-y' ? touchAngle > 45 : 90 - touchAngle > 45 ;
259255 }
260- if ( e . cancelable ) {
261- e . preventDefault ( ) ;
256+ if ( isScrolling === false ) {
257+ el . classList . add ( 'atropos-rotate-touch' ) ;
258+ if ( e . cancelable ) {
259+ e . preventDefault ( ) ;
260+ }
262261 }
263262 }
263+ if ( e . pointerType !== 'mouse' && isScrolling ) {
264+ return ;
265+ }
264266 const rotateXPercentage = ( rotateX / params . rotateXMax ) * 100 ;
265267 const rotateYPercentage = ( rotateY / params . rotateYMax ) * 100 ;
266268
@@ -277,16 +279,24 @@ function Atropos(originalParams = {}) {
277279 ) ;
278280
279281 if ( highlightEl ) {
280- $setDuration ( highlightEl , '0ms' ) ;
282+ $setDuration ( highlightEl , `${ params . duration } ms` ) ;
283+ $setEasing ( highlightEl , 'ease-out' ) ;
281284 $setTransform (
282285 highlightEl ,
283286 `translate3d(${ - rotateYPercentage * 0.25 } %, ${ rotateXPercentage * 0.25 } %, 0)` ,
284287 ) ;
285- highlightEl . style . opacity =
286- Math . max ( Math . abs ( rotateXPercentage ) , Math . abs ( rotateYPercentage ) ) / 100 ;
288+ $setOpacity (
289+ highlightEl ,
290+ Math . max ( Math . abs ( rotateXPercentage ) , Math . abs ( rotateYPercentage ) ) / 100 ,
291+ ) ;
287292 }
288293
289- setChildrenOffset ( { rotateXPercentage, rotateYPercentage, duration : '0ms' } ) ;
294+ setChildrenOffset ( {
295+ rotateXPercentage,
296+ rotateYPercentage,
297+ duration : `${ params . duration } ms` ,
298+ easeOut : true ,
299+ } ) ;
290300
291301 if ( typeof params . onRotate === 'function' ) params . onRotate ( rotateX , rotateY ) ;
292302 } ;
@@ -297,24 +307,29 @@ function Atropos(originalParams = {}) {
297307 if ( ! self . isActive ) return ;
298308 if ( e && e . type === 'pointerup' && e . pointerType === 'mouse' ) return ;
299309 if ( e && e . type === 'pointerleave' && e . pointerType !== 'mouse' ) return ;
300- if ( typeof params . rotateTouch === 'string' && rotated ) {
310+ if ( typeof params . rotateTouch === 'string' && isScrolling ) {
301311 el . classList . remove ( 'atropos-rotate-touch' ) ;
302312 }
303- el . classList . remove ( 'atropos-active' ) ;
313+ queue . push ( ( ) => el . classList . remove ( 'atropos-active' ) ) ;
314+ $setDuration ( scaleEl , `${ params . duration } ms` ) ;
315+ $setEasing ( scaleEl , '' ) ;
304316 $setTransform ( scaleEl , `translate3d(0,0, ${ 0 } px)` ) ;
305- $setDuration ( scaleEl , `${ params . durationLeave } ms` ) ;
306317 if ( shadowEl ) {
307- $setDuration ( shadowEl , `${ params . durationLeave } ms` ) ;
318+ $setDuration ( shadowEl , `${ params . duration } ms` ) ;
319+ $setEasing ( shadowEl , '' ) ;
308320 }
309321 if ( highlightEl ) {
310- $setDuration ( highlightEl , `${ params . durationLeave } ms` ) ;
322+ $setDuration ( highlightEl , `${ params . duration } ms` ) ;
323+ $setEasing ( highlightEl , '' ) ;
311324 $setTransform ( highlightEl , `translate3d(0, 0, 0)` ) ;
312- highlightEl . style . opacity = 0 ;
325+ $setOpacity ( highlightEl , 0 ) ;
313326 }
314- $setDuration ( rotateEl , `${ params . durationLeave } ms` ) ;
327+ $setDuration ( rotateEl , `${ params . duration } ms` ) ;
328+ $setEasing ( rotateEl , '' ) ;
315329 $setTransform ( rotateEl , `translate3d(0,0,0) rotateX(0deg) rotateY(0deg)` ) ;
316330
317- setChildrenOffset ( { duration : `${ params . durationLeave } ms` } ) ;
331+ setChildrenOffset ( { duration : `${ params . duration } ms` } ) ;
332+
318333 self . isActive = false ;
319334 if ( typeof params . onRotate === 'function' ) params . onRotate ( 0 , 0 ) ;
320335 if ( typeof params . onLeave === 'function' ) params . onLeave ( ) ;
@@ -387,6 +402,7 @@ function Atropos(originalParams = {}) {
387402
388403 const destroy = ( ) => {
389404 self . destroyed = true ;
405+ cancelAnimationFrame ( queueFrameId ) ;
390406 $off ( document , 'click' , onDocumentClick ) ;
391407 $off ( eventsEl , 'pointerdown' , onPointerEnter ) ;
392408 $off ( eventsEl , 'pointerenter' , onPointerEnter ) ;
0 commit comments