Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* tslint:disable:variable-name */
- import {Inject, Injectable, PLATFORM_ID} from '@angular/core';
- import {isPlatformBrowser} from '@angular/common';
- @Injectable({
- providedIn: 'root'
- })
- export class PerspectiveCardService {
- // --------------------------------------------------------------------
- // VARIABLES DECLARATIONS
- // --------------------------------------------------------------------
- private EPSILON = 0.001;
- private _element: HTMLElement | null = null;
- private _debug: any;
- private _zoomSize: any;
- private _intensity: any;
- private _ambient: any;
- private transformer: any;
- private shine: any;
- private _playing: any;
- private observer: IntersectionObserver;
- private _lastFrameTime: any;
- private _lastDelta: any;
- private _delta: any;
- private _motionOff: boolean;
- private _pointerControlled: any;
- private _tPoint = [0, 0, -800];
- private _zoom: any;
- private _center: any;
- private _lookDifferential: any;
- private _lookPoint = [0, 0, -800];
- private _axis: any;
- private _position = [0, 0];
- private _size: any;
- private debounceTimer: any;
- private isBrowser: boolean;
- // --------------------------------------------------------------------
- // CONSTRUCTOR
- // --------------------------------------------------------------------
- constructor(element, settings: any = {}, isBrowser) {
- this.isBrowser = isBrowser;
- if (this.isBrowser) {
- // Set the element
- this.element = element;
- // set settings
- this.debug =
- settings.debug || this.element.hasAttribute('data-debug') || false;
- this.zoomSize =
- settings.zoom || +this.element.getAttribute('data-zoom') || 40;
- this.intensity =
- settings.intensity ||
- this.element.getAttribute('data-intensity') ||
- 10;
- this.ambient = -1;
- if (settings.ambient !== undefined && settings.ambient !== false) {
- const settingsVal = settings.ambient;
- if (settingsVal === true) {
- this.ambient = 0;
- } else {
- this.ambient = settingsVal;
- }
- } else if (this.element.hasAttribute('data-ambient')) {
- const dataVal = this.element.getAttribute('data-ambient');
- if (dataVal !== 'false') {
- if (dataVal === '' || dataVal === 'true') {
- this.ambient = 0;
- } else {
- this.ambient = +dataVal;
- }
- }
- }
- // Find the transformer and shine elements. We save these so we
- // don't waste proc time doing it every frame
- this.transformer = this.element.querySelector(
- '.perspective-card__transformer'
- );
- this.shine = this.element.querySelector('.perspective-card__shine');
- // Add event listeners for resize, scroll, pointer enter and leave
- window.addEventListener('resize', this.resize.bind(this));
- window.addEventListener('scroll', this.resize.bind(this));
- this.element.addEventListener('pointerenter', this.pointerEnter.bind(this));
- this.element.addEventListener('pointerleave', this.pointerLeave.bind(this));
- if (this.ambient >= 0) {
- // Set up and bind the intersection observer
- this.observer = new IntersectionObserver(this.intersect, {
- rootMargin: '0%',
- threshold: [0.1]
- });
- this.observer.observe(this.element);
- }
- // Initial resize to find the location and dimensions of the element
- this.resize();
- }
- }
- // --------------------------------------------------------------------
- // GETTER AND SETTER
- // --------------------------------------------------------------------
- set motionOff(value: boolean) {
- this._motionOff = value;
- }
- get motionOff(): boolean {
- return this._motionOff === true;
- }
- set element(value: HTMLElement) {
- if (value instanceof HTMLElement) {
- this._element = value;
- }
- }
- get element(): HTMLElement | null {
- return this._element;
- }
- set position(value: number[]) {
- if (value instanceof Array && value.length >= 2) {
- this._position = value;
- }
- }
- get position(): number[] {
- return this._position;
- }
- set tPoint(value: number[]) {
- if (value instanceof Array && value.length >= 3) {
- this._tPoint = value;
- this.calculateLookDifferential();
- }
- }
- get tPoint(): number[] {
- return this._tPoint;
- }
- set lookPoint(value: number[]) {
- if (value instanceof Array && value.length >= 3) {
- this.calculateLookDifferential();
- this._lookPoint = value;
- }
- }
- get lookPoint(): number[] {
- return this._lookPoint;
- }
- set center(value: number[]) {
- if (value instanceof Array && value.length >= 3) {
- this._center = value;
- }
- }
- get center(): number[] {
- return this._center || [0, 0, 0];
- }
- set zoom(value: any) {
- // @ts-ignore
- if (!isNaN(value)) {
- this._zoom = value;
- }
- }
- get zoom(): any {
- return this._zoom || 0;
- }
- set zoomSize(value: number) {
- if (!isNaN(value)) {
- this._zoomSize = value;
- }
- }
- get zoomSize(): number {
- return this._zoomSize || 40;
- }
- set intensity(value: number) {
- if (!isNaN(value)) {
- this._intensity = value;
- }
- }
- get intensity(): number {
- return this._intensity || 10;
- }
- set size(value: number[]) {
- if (value instanceof Array && value.length >= 2) {
- this._size = value;
- }
- }
- get size(): number[] {
- return this._size || [0, 0];
- }
- set debug(value: boolean) {
- this._debug = value;
- }
- get debug(): boolean {
- return this._debug || false;
- }
- set ambient(value: any) {
- this._ambient = value;
- }
- get ambient(): any {
- return this._ambient || false;
- }
- set axis(value: number[]) {
- if (value instanceof Array && value.length >= 2) {
- this._axis = value;
- }
- }
- get axis(): number[] {
- return this._axis || [0, 0];
- }
- set playing(value: boolean) {
- if (!this.playing && value === true) {
- // Reset last frame time
- this.lastFrameTime = 0;
- requestAnimationFrame(this.play.bind(this));
- }
- this._playing = value === true;
- }
- get playing(): boolean {
- return this._playing === true;
- }
- set lastFrameTime(value: number) {
- if (+value) {
- this._lastFrameTime = value;
- }
- }
- get lastFrameTime(): number {
- return this._lastFrameTime || 0;
- }
- set delta(value: number) {
- if (+value) {
- this._delta = value;
- }
- }
- get delta(): number {
- return this._delta || 0;
- }
- set lastDelta(value: number) {
- if (+value) {
- this._lastDelta = value;
- }
- }
- get lastDelta(): number {
- return this._lastDelta || 0;
- }
- set pointerControlled(value: boolean) {
- if (!this.pointerControlled && value === true) {
- window.addEventListener('pointermove', this.pointerMove.bind(this));
- } else if (this.pointerControlled && value === false) {
- window.removeEventListener('pointermove', this.pointerMove.bind(this));
- }
- this._pointerControlled = value;
- }
- get pointerControlled(): boolean {
- return this._pointerControlled === true;
- }
- // --------------------------------------------------------------------
- // FUNCTIONS
- // --------------------------------------------------------------------
- public play(delta, raf = true): void {
- // If `playing` is true, then request the animation frame again
- if (this.playing && raf === true) {
- requestAnimationFrame(this.play.bind(this));
- }
- // Set the last frame time in order to derive the sensible delta
- this.lastFrameTime = Math.max(16, Math.min(32, delta - this.lastDelta));
- this.lastDelta = delta;
- this.delta += this.lastFrameTime;
- if (this.motionOff) {
- return;
- }
- // Set the divisor for animations based on the last frame time
- const divisor = 1 / this.lastFrameTime;
- // if (isNaN(divisor) || divisor === Infinity) divisor = 1;
- // If this element is not pointer controlled then we want to animate
- // the ambient target point value around somehow. Here we use a simple
- // fourier simulation.
- if (!this.pointerControlled) {
- // const d = delta * 0.0005;
- // const a = 1.8 + Math.sin(2. * d + .2) + .4 * Math.cos(4. * 2. * d);
- // const l = a * 80.;
- const d = this.delta * 0.0001 + this.ambient;
- const s = Math.sin(d * 2);
- const c = Math.cos(d * 0.5);
- const l = this.intensity * 10 * Math.cos(d * 3.542 + 1234.5);
- // Some really arbitrary numbers here. They don't mean anythign in particular, they just work.
- this.tPoint = [c * l, s * l, this.tPoint[2]];
- }
- // If our zoom differential (the different between the zoom and
- // target zoom) is greater than the EPS value. We should animate it
- if (Math.abs(this.zoom - this.center[2]) > this.EPSILON) {
- this.center = [
- this.center[0],
- this.center[1],
- this.center[2] + (this.zoom - this.center[2]) * (divisor * 2)
- ];
- }
- // If our look differential (the difference between the look
- // point and the target point) is greater than 2 then we should
- // animate it. We use a relatively arbitrary value of 2 here
- // because we're using the square of the distance (to save
- // unecessary calculation) here.
- if (this._lookDifferential > 2) {
- this.lookPoint = [
- this.lookPoint[0] +
- (this.tPoint[0] - this.lookPoint[0]) * (divisor * 2),
- this.lookPoint[1] +
- (this.tPoint[1] - this.lookPoint[1]) * (divisor * 2),
- this.lookPoint[2] + (this.tPoint[2] - this.lookPoint[2]) * (divisor * 2)
- ];
- }
- // Find the wold matrix using the targetTo method (see above)
- const worldMatrix = PerspectiveCardService.targetTo(this.center, this.lookPoint, [
- 0,
- 1,
- 0
- ]);
- // Find the polar coordinates for the rendition of the gradient.
- const angle =
- Math.atan2(this.lookPoint[1], this.lookPoint[0]) + Math.PI * 0.5;
- const len = Math.hypot(this.lookPoint[0], this.lookPoint[1]);
- // Transform the transformer element using the calculated values
- const matrix = `matrix3d(${worldMatrix[0]},${worldMatrix[1]},${worldMatrix[2]},${worldMatrix[3]},${worldMatrix[4]},${worldMatrix[5]},${worldMatrix[6]},${worldMatrix[7]},${worldMatrix[8]},${worldMatrix[9]},${worldMatrix[10]},${worldMatrix[11]},${worldMatrix[12]},${worldMatrix[13]},${worldMatrix[14]},${worldMatrix[15]})`;
- this.transformer.style.transform = matrix;
- // Draw the gradient using the polar coordinates.
- this.shine.style.background = `linear-gradient(${angle}rad, rgba(255,255,255,${Math.max(
- 0.01,
- Math.abs(len * 0.002)
- )}) 0%, rgba(255,255,255,${Math.max(
- 0.01,
- Math.abs(len * 0.002)
- )}) 5%, rgba(255,255,255,0) 80%)`;
- }
- public calculateLookDifferential(): void {
- const d = [
- this.lookPoint[0] - this.tPoint[0],
- this.lookPoint[1] - this.tPoint[1],
- this.lookPoint[2] - this.tPoint[2]
- ];
- this._lookDifferential = d[0] * d[0] + d[1] * d[1] + d[2] * d[2];
- }
- public pointerMove(e): void {
- this.tPoint = [
- e.clientX - this.axis[0],
- e.clientY - this.axis[1],
- this.tPoint[2]
- ];
- }
- public pointerEnter(): void {
- this.pointerControlled = true;
- this.zoom = this.zoomSize;
- this.element.classList.add('perspective-card--over');
- if (this.ambient === false) {
- this.playing = true;
- }
- }
- public pointerLeave(): void {
- this.pointerControlled = false;
- this.zoom = 0;
- this.element.classList.remove('perspective-card--over');
- if (this.ambient < 0) {
- this.playing = false;
- setTimeout(() => {
- this.transformer.style.transform = `matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)`;
- this.shine.style.background = `none`;
- }, 100);
- }
- }
- public resize(): void {
- const resize = () => {
- const pos = this.element.getBoundingClientRect();
- this.position = [pos.left, pos.top];
- this.size = [pos.width, pos.height];
- this.axis = [
- this.position[0] + this.size[0] * 0.5,
- this.position[1] + this.size[1] * 0.5
- ];
- };
- clearTimeout(this.debounceTimer);
- this.debounceTimer = setTimeout(resize, 300);
- }
- public intersect(entries, observer): void {
- // Loop through the entries and set up the playing state based on whether the element is onscreen or not.
- entries.forEach((entry, i) => {
- if (entry.isIntersecting) {
- this.playing = true;
- } else {
- this.playing = false;
- }
- });
- }
- // tslint:disable-next-line:member-ordering
- static targetTo(eye, target, up): any {
- if (eye.array) {
- eye = eye.array;
- }
- if (target.array) {
- target = target.array;
- }
- if (up.array) {
- up = up.array;
- }
- if (eye.length && eye.length >= 3 && target.length && target.length >= 3 && up.length && up.length >= 3) {
- const e = {x: eye[0], y: eye[1], z: eye[2]};
- const c = {x: target[0], y: target[1], z: target[2]};
- const u = {x: up[0], y: up[1], z: up[2]};
- const off = {x: e.x - c.x, y: e.y - c.y, z: e.z - c.z};
- let l = off.x * off.x + off.y * off.y + off.z * off.z;
- if (l > 0) {
- l = 1 / Math.sqrt(l);
- off.x *= l;
- off.y *= l;
- off.z *= l;
- }
- const or = {x: u.y * off.z - u.z * off.y, y: u.z * off.x - u.x * off.z, z: u.x * off.y - u.y * off.x};
- l = or.x * or.x + or.y * or.y + or.z * or.z;
- if (l > 0) {
- l = 1 / Math.sqrt(l);
- or.x *= l;
- or.y *= l;
- or.z *= l;
- }
- return [
- or.x,
- or.y,
- or.z,
- 0,
- off.y * or.z - off.z * or.y,
- off.z * or.x - off.x * or.z,
- off.x * or.y - off.y * or.x,
- 0,
- off.x,
- off.y,
- off.z,
- 0,
- e.x,
- e.y,
- e.z,
- 1
- ];
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement