UNPKG

iportal

Version:

web-portal

270 lines (257 loc) 7.59 kB
'use strict' // 合抱之木,生於毫末;九层之台,起於累土;千里之行,始於足下。 import ease from './ease' import { EventProvider } from '../Event' const rAF = requestAnimationFrame ?? setTimeout const gCS = getComputedStyle /* Animate */ class Animate extends EventProvider { public el!: HTMLElement private _props = {} private _transforms = {} private _transforming = false private _proper: Array<object> = [] private _caller: Array<Function> = [] private _transitionProps: Array<string> = [] constructor (el: HTMLElement) { super() if (!el) return this.el = el this.el.style.setProperty('transition-duration', '0ms') } public transform (transform: string) { this.transition('transform') const propName = (transform.match(/\w+\b/) || [])[0] if (propName) this._transforms[propName] = transform return this } public skew (x: number, y: number) { return this.transform('skew(' + x + 'deg, ' + (y || 0) + 'deg)') } public skewX (n: number) { return this.transform('skewX(' + n + 'deg)') } public skewY (n: number) { return this.transform('skewY(' + n + 'deg)') } public to (x: number = 0, y: number = 0, z: number = 0) { // 3d set this.transform('translate3d(' + (x ? x + 'px' : 0) + ',' + (y ? y + 'px' : 0) + ',' + (z ? z + 'px' : 0) + ')') return this } public translate = this.to public translate3d = this.to public x (n: number) { return this.transform('translateX(' + n + 'px)') } public translateX = this.x public y (n: number) { return this.transform('translateY(' + n + 'px)') } public translateY = this.y public z (z: number) { return this.transform('translateZ(' + z + 'px)') } public translateZ = this.z public scale (x: number, y: number = x) { return this.transform('scale(' + x + ', ' + (y || x) + ')') } public opacity (o: number) { this.transition('opacity') return this.style('opacity', o) } public scaleX (n: number) { return this.transform('scaleX(' + n + ')') } public matrix (m11: number, m12: number, m21: number, m22: number, m31: number, m32: number) { return this.transform('matrix(' + [m11, m12, m21, m22, m31, m32].join(',') + ')') } public scaleY (n: number) { return this.transform('scaleY(' + n + ')') } public rotate (n: number) { return this.transform('rotate(' + n + 'deg)') } public rotateX (n: number) { return this.transform('rotateX(' + n + 'deg)') } public rotateY (n: number) { return this.transform('rotateY(' + n + 'deg)') } public rotateZ (n: number) { return this.transform('rotateZ(' + n + 'deg)') } public rotate3d (x: number, y: number, z: number, d: number) { return this.transform('rotate3d(' + x + ', ' + y + ',' + z + ',' + d + 'deg)') } public perspective (z: number) { const box = this.el.parentElement if (box) { box.style.setProperty('transform-style', 'preserve-3d') box.style.setProperty('perspective', z + 'px') } return this } public backface (visibility: boolean = true) { return this.style('backface-visibility', visibility ? 'visible' : 'hidden') } public ease (s: string) { s = ease[s as string] || s || 'ease' return this.style('transition-timing-function', s) } public animate (name: string, props: object) { for (const i in props) { if (props.hasOwnProperty(i)) { this.style('animation-' + i, props[i]) } } return this.style('animation-name', name) } public duration (n: string | number) { n = 'string' === typeof n ? parseFloat(n) * 1000 : n return this.style('transition-duration', n + 'ms') } public getDuration () { return !!parseFloat(gCS(this.el).transitionDuration) } public delay (n: string | number) { n = 'string' === typeof n ? parseFloat(n) * 1000 : n return this.style('transition-delay', n + 'ms') } public origin (x: number | string | Array<number>, y: number = 0) { let n = 'center' if (Array.isArray(x)) { y = x[1] || 0 x = x[0] || 0 } if (typeof x === 'string') { n = x } else if (typeof x === 'number') { n = x + 'px' + ' ' + y + 'px' } return this.style('transform-origin', n) } public width (val?: number) { this.transition('width') return this.style('width', val === undefined ? '' : val + 'px') } public height (val?: number) { this.transition('height') return this.style('height', val === undefined ? '' : val + 'px') } public add (prop: string, val: number) { return this.on('start', () => { const curr = parseInt(this.current(prop), 10) this.style(prop, curr + val + 'px') }) } public subc (prop: string, val: number) { return this.on('start', () => { const curr = parseInt(this.current(prop), 10) this.style(prop, curr - val + 'px') }) } public current (prop: string) { return gCS(this.el).getPropertyValue(prop) } public transition (prop: string) { if (this._transitionProps.indexOf(prop) === -1) this._transitionProps.push(prop) return this } public filter (val: string) { this.style('filter', val) this.transition('filter') return this } public style (prop: string, val?: string | number) { this._props[prop] = val === undefined ? '' : val return this } private onceTransitionend (fn: () => void) { if (!this.getDuration()) return setTimeout(fn, 0) const onec = (e: TransitionEvent) => { if (e.target !== this.el) return false fn() this.el.removeEventListener('transitionend', onec, false) } this.el.addEventListener('transitionend', onec, false) } private applyTransform () { const transform: Array<string> = [] if (!this._transforms['translate3d'] && !this._transforms['translateZ']) { this._transforms['translateZ'] = 'translateZ(0)' } for (const i in this._transforms) { transform.push(this._transforms[i]) } if (transform.length) { this.style('transform', transform.join(' ')) } return this } public applyProperties () { const prop = this._proper.shift() this._transforming = true for (const name in prop) { this.el.style.setProperty(name, prop[name]) } this.onceTransitionend(() => { this.clear() this.next() }) return this } public next () { if (this._caller.length) { const fn = this._caller.shift() fn?.() } if (this._caller.length === 0) { this.init() } else { rAF(() => { this.applyProperties() }) } } public clear () { this._transforms = {} } public init () { if (this.getDuration()) { this.el.style.setProperty('transition-duration', '0ms') } this.clear() this._transforming = false return this } public then (fn: () => void = () => undefined) { this.applyTransform() this.style('transition-property', this._transitionProps.join(', ')) this._proper.push(this._props) this._props = {} this._caller.push(() => fn?.call(this)) return this } public and (fn: () => void = () => undefined) { this.then(fn) if (this._transforming) return this rAF(() => { this.applyProperties() }) } public end (fn?: () => void) { return new Promise<void>((resolve) => { fn = fn ?? (() => resolve()) this.then(fn) if (this._transforming) return rAF(() => { this.applyProperties() }) }) } } export { Animate }