Skip to content

Commit

Permalink
feat: "always active" functionality, stretchZ and commonOrigin
Browse files Browse the repository at this point in the history
  • Loading branch information
nolimits4web committed Oct 15, 2021
1 parent 533d0dc commit 78c177c
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 64 deletions.
1 change: 1 addition & 0 deletions src/atropos.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ interface CSSSelector extends String {}
export interface AtroposOptions {
el?: HTMLElement | CSSSelector;
eventsEl?: HTMLElement | CSSSelector;
alwaysActive?: boolean;
activeOffset?: number;
shadowOffset?: number;
shadowScale?: number;
Expand Down
172 changes: 108 additions & 64 deletions src/atropos.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ function Atropos(originalParams = {}) {
const self = {
__atropos__: true,
params: {
alwaysActive: true,
activeOffset: 50,
shadowOffset: 50,
shadowScale: 1,
Expand All @@ -28,6 +29,8 @@ function Atropos(originalParams = {}) {
rotateYInvert: false,
stretchX: 0,
stretchY: 0,
stretchZ: 0,
commonOrigin: true,
shadow: true,
highlight: true,
onEnter: null,
Expand Down Expand Up @@ -85,6 +88,9 @@ function Atropos(originalParams = {}) {
const $setOpacity = (element, value) => {
queue.push({ element, prop: 'opacity', value });
};
const $setOrigin = (element, value) => {
queue.push({ element, prop: 'transformOrigin', value });
};
const $on = (element, event, handler, props) => element.addEventListener(event, handler, props);
const $off = (element, event, handler, props) =>
element.removeEventListener(event, handler, props);
Expand Down Expand Up @@ -162,53 +168,25 @@ function Atropos(originalParams = {}) {
});
};

const onPointerEnter = (e) => {
isScrolling = undefined;
if (e.type === 'pointerdown' && e.pointerType === 'mouse') return;
if (e.type === 'pointerenter' && e.pointerType !== 'mouse') return;
if (e.type === 'pointerdown') {
e.preventDefault();
}
clientXStart = e.clientX;
clientYStart = e.clientY;
queue.push(() => el.classList.add('atropos-active'));
$setDuration(rotateEl, `${params.duration}ms`);
$setEasing(rotateEl, 'ease-out');
$setTransform(scaleEl, `translate3d(0,0, ${params.activeOffset}px)`);
$setDuration(scaleEl, `${params.duration}ms`);
$setEasing(scaleEl, 'ease-out');
if (shadowEl) {
$setDuration(shadowEl, `${params.duration}ms`);
$setEasing(shadowEl, 'ease-out');
}

self.isActive = true;
if (typeof params.onEnter === 'function') params.onEnter();
};

const onTouchMove = (e) => {
if (isScrolling === false && e.cancelable) {
e.preventDefault();
}
};

const onPointerMove = (e) => {
if (!params.rotate || !self.isActive) return;
if (e.pointerType !== 'mouse') {
if (!params.rotateTouch) return;
e.preventDefault();
}
const { clientX, clientY } = e;
const setElements = (clientX, clientY) => {
const isMultiple = el !== eventsEl;
if (!elBoundingClientRect) {
elBoundingClientRect = el.getBoundingClientRect();
}
if (el !== eventsEl && !eventsElBoundingClientRect) {
if (isMultiple && !eventsElBoundingClientRect) {
eventsElBoundingClientRect = eventsEl.getBoundingClientRect();
}
if (typeof clientX === 'undefined' && typeof clientY === 'undefined') {
const rect = isMultiple ? eventsElBoundingClientRect : elBoundingClientRect;
clientX = rect.left + rect.width / 2;
clientY = rect.top + rect.height / 2;
}

let rotateX = 0;
let rotateY = 0;
const { top, left, width, height } = elBoundingClientRect;
if (el === eventsEl) {
let transformOrigin;
if (!isMultiple) {
const centerX = width / 2;
const centerY = height / 2;

Expand All @@ -235,48 +213,33 @@ function Atropos(originalParams = {}) {

rotateY = ((params.rotateYMax * (coordX - centerX)) / (parentWidth - width / 2)) * -1;
rotateX = (params.rotateXMax * (coordY - centerY)) / (parentHeight - height / 2);
transformOrigin = `${clientX - left}px ${clientY - top}px`;
}

rotateX = Math.min(Math.max(-rotateX, -params.rotateXMax), params.rotateXMax);
if (params.rotateXInvert) rotateX = -rotateX;
rotateY = Math.min(Math.max(-rotateY, -params.rotateYMax), params.rotateYMax);
if (params.rotateYInvert) rotateY = -rotateY;

if (
typeof params.rotateTouch === 'string' &&
(rotateX !== 0 || rotateY !== 0) &&
typeof isScrolling === 'undefined'
) {
const diffX = clientX - clientXStart;
const diffY = clientY - clientYStart;
if (diffX * diffX + diffY * diffY >= 25) {
const touchAngle = (Math.atan2(Math.abs(diffY), Math.abs(diffX)) * 180) / Math.PI;
isScrolling = params.rotateTouch === 'scroll-y' ? touchAngle > 45 : 90 - touchAngle > 45;
}
if (isScrolling === false) {
el.classList.add('atropos-rotate-touch');
if (e.cancelable) {
e.preventDefault();
}
}
}
if (e.pointerType !== 'mouse' && isScrolling) {
return;
}
const rotateXPercentage = (rotateX / params.rotateXMax) * 100;
const rotateYPercentage = (rotateY / params.rotateYMax) * 100;

const stretchX =
(el !== eventsEl ? (rotateYPercentage / 100) * params.stretchX : 0) *
(isMultiple ? (rotateYPercentage / 100) * params.stretchX : 0) *
(params.rotateYInvert ? -1 : 1);
const stretchY =
(el !== eventsEl ? (rotateXPercentage / 100) * params.stretchY : 0) *
(isMultiple ? (rotateXPercentage / 100) * params.stretchY : 0) *
(params.rotateXInvert ? -1 : 1);

const stretchZ = isMultiple
? (Math.max(Math.abs(rotateXPercentage), Math.abs(rotateYPercentage)) / 100) * params.stretchZ
: 0;
$setTransform(
rotateEl,
`translate3d(${stretchX}%, ${-stretchY}%, 0px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`,
`translate3d(${stretchX}%, ${-stretchY}%, ${-stretchZ}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`,
);
if (transformOrigin && params.commonOrigin) {
$setOrigin(rotateEl, transformOrigin);
}

if (highlightEl) {
$setDuration(highlightEl, `${params.duration}ms`);
Expand All @@ -301,6 +264,74 @@ function Atropos(originalParams = {}) {
if (typeof params.onRotate === 'function') params.onRotate(rotateX, rotateY);
};

const activate = () => {
queue.push(() => el.classList.add('atropos-active'));
$setDuration(rotateEl, `${params.duration}ms`);
$setEasing(rotateEl, 'ease-out');
$setTransform(scaleEl, `translate3d(0,0, ${params.activeOffset}px)`);
$setDuration(scaleEl, `${params.duration}ms`);
$setEasing(scaleEl, 'ease-out');
if (shadowEl) {
$setDuration(shadowEl, `${params.duration}ms`);
$setEasing(shadowEl, 'ease-out');
}

self.isActive = true;
};

const onPointerEnter = (e) => {
isScrolling = undefined;
if (e.type === 'pointerdown' && e.pointerType === 'mouse') return;
if (e.type === 'pointerenter' && e.pointerType !== 'mouse') return;
if (e.type === 'pointerdown') {
e.preventDefault();
}
clientXStart = e.clientX;
clientYStart = e.clientY;

if (params.alwaysActive) return;
activate();
if (typeof params.onEnter === 'function') params.onEnter();
};

const onTouchMove = (e) => {
if (isScrolling === false && e.cancelable) {
e.preventDefault();
}
};

const onPointerMove = (e) => {
if (!params.rotate || !self.isActive) return;
if (e.pointerType !== 'mouse') {
if (!params.rotateTouch) return;
e.preventDefault();
}
const { clientX, clientY } = e;

const diffX = clientX - clientXStart;
const diffY = clientY - clientYStart;
if (
typeof params.rotateTouch === 'string' &&
(diffX !== 0 || diffY !== 0) &&
typeof isScrolling === 'undefined'
) {
if (diffX * diffX + diffY * diffY >= 25) {
const touchAngle = (Math.atan2(Math.abs(diffY), Math.abs(diffX)) * 180) / Math.PI;
isScrolling = params.rotateTouch === 'scroll-y' ? touchAngle > 45 : 90 - touchAngle > 45;
}
if (isScrolling === false) {
el.classList.add('atropos-rotate-touch');
if (e.cancelable) {
e.preventDefault();
}
}
}
if (e.pointerType !== 'mouse' && isScrolling) {
return;
}
setElements(clientX, clientY);
};

const onPointerLeave = (e) => {
elBoundingClientRect = undefined;
eventsElBoundingClientRect = undefined;
Expand All @@ -310,6 +341,14 @@ function Atropos(originalParams = {}) {
if (typeof params.rotateTouch === 'string' && isScrolling) {
el.classList.remove('atropos-rotate-touch');
}

if (params.alwaysActive) {
setElements();
if (typeof params.onRotate === 'function') params.onRotate(0, 0);
if (typeof params.onLeave === 'function') params.onLeave();
return;
}

queue.push(() => el.classList.remove('atropos-active'));
$setDuration(scaleEl, `${params.duration}ms`);
$setEasing(scaleEl, '');
Expand Down Expand Up @@ -398,6 +437,11 @@ function Atropos(originalParams = {}) {
$on(eventsEl, 'pointerleave', onPointerLeave);
$on(eventsEl, 'pointerup', onPointerLeave);
$on(eventsEl, 'lostpointercapture', onPointerLeave);

if (params.alwaysActive) {
activate();
setElements();
}
};

const destroy = () => {
Expand Down
2 changes: 2 additions & 0 deletions src/react/atropos-react.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ const paramsKeys = [
'rotateYInvert',
'stretchX',
'stretchY',
'stretchZ',
'commonOrigin',
'shadow',
'highlight',
'onEnter',
Expand Down
6 changes: 6 additions & 0 deletions src/svelte/atropos-svelte.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
export let innerClass = '';
export let eventsEl = undefined;
export let alwaysActive = undefined;
export let activeOffset = undefined;
export let shadowOffset = undefined;
export let shadowScale = undefined;
Expand All @@ -21,6 +22,8 @@
export let rotateYInvert = undefined;
export let stretchX = undefined;
export let stretchY = undefined;
export let stretchZ = undefined;
export let commonOrigin = true;
export let shadow = true;
export let highlight = true;
Expand All @@ -37,6 +40,7 @@
atroposRef = AtroposCore({
el: elRef,
eventsEl,
alwaysActive,
activeOffset,
shadowOffset,
shadowScale,
Expand All @@ -49,6 +53,8 @@
rotateYInvert,
stretchX,
stretchY,
stretchZ,
commonOrigin,
onEnter() {
emit('enter');
},
Expand Down
3 changes: 3 additions & 0 deletions src/vue/atropos-vue.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const Atropos = {
rotateClass: String,

eventsEl: { type: [String, Object], default: undefined },
alwaysActive: { type: Boolean, default: false },
activeOffset: { type: Number, default: undefined },
shadowOffset: { type: Number, default: undefined },
shadowScale: { type: Number, default: undefined },
Expand All @@ -22,6 +23,8 @@ const Atropos = {
rotateYInvert: { type: Boolean, default: undefined },
stretchX: { type: Number, default: undefined },
stretchY: { type: Number, default: undefined },
stretchZ: { type: Number, default: undefined },
commonOrigin: { type: Boolean, default: true },
shadow: { type: Boolean, default: true },
highlight: { type: Boolean, default: true },
},
Expand Down

0 comments on commit 78c177c

Please sign in to comment.