UNPKG

scroll-timeline-polyfill

Version:

A polyfill for scroll-driven animations on the web via ScrollTimeline

261 lines (224 loc) 6.43 kB
// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import {createAType, invertType, multiplyTypes, parseCSSNumericValue, to, toSum} from './numeric-values'; import {simplifyCalculation} from './simplify-calculation'; import './tokenizer' export function installCSSOM() { // Object for storing details associated with an object which are to be kept // private. This approach allows the constructed objects to more closely // resemble their native counterparts when inspected. let privateDetails = new WeakMap(); function displayUnit(unit) { switch(unit) { case 'percent': return '%'; case 'number': return ''; default: return unit.toLowerCase(); } } function toCssUnitValue(v) { if (typeof v === 'number') return new CSSUnitValue(v, 'number'); return v; } function toCssNumericArray(values) { const result = []; for (let i = 0; i < values.length; i++) { result[i] = toCssUnitValue(values[i]); } return result; } class CSSNumericValue { static parse(value) { if (value instanceof CSSNumericValue) return value; return simplifyCalculation(parseCSSNumericValue(value), {}); } // TODO: Add other methods: add, sub, mul, div, … // Spec: https://drafts.css-houdini.org/css-typed-om/#numeric-value } class CSSMathValue extends CSSNumericValue { constructor(values, operator, opt_name, opt_delimiter) { super(); privateDetails.set(this, { values: toCssNumericArray(values), operator: operator, name: opt_name || operator, delimiter: opt_delimiter || ', ' }); } get operator() { return privateDetails.get(this).operator; } get values() { return privateDetails.get(this).values; } toString() { const details = privateDetails.get(this); return `${details.name}(${details.values.join(details.delimiter)})`; } } const cssOMTypes = { 'CSSNumericValue': CSSNumericValue, 'CSSMathValue': CSSMathValue, 'CSSUnitValue': class extends CSSNumericValue { constructor(value, unit) { super(); privateDetails.set(this, { value: value, unit: unit }); } get value() { return privateDetails.get(this).value; } set value(value) { privateDetails.get(this).value = value; } get unit() { return privateDetails.get(this).unit; } to(unit) { return to(this, unit) } toSum(...units) { return toSum(this, ...units) } type() { const details = privateDetails.get(this) // The type of a CSSUnitValue is the result of creating a type from its unit internal slot. return createAType(details.unit) } toString() { const details = privateDetails.get(this); return `${details.value}${displayUnit(details.unit)}`; } }, 'CSSKeywordValue': class { constructor(value) { this.value = value; } toString() { return this.value.toString(); } }, 'CSSMathSum': class extends CSSMathValue { constructor(values) { super(arguments, 'sum', 'calc', ' + '); } }, 'CSSMathProduct': class extends CSSMathValue { constructor(values) { super(arguments, 'product', 'calc', ' * '); } toSum(...units) { return toSum(this, ...units) } type() { const values = privateDetails.get(this).values; // The type is the result of multiplying the types of each of the items in its values internal slot. return values.map(v => v.type()).reduce(multiplyTypes) } }, 'CSSMathNegate': class extends CSSMathValue { constructor(values) { super([arguments[0]], 'negate', '-'); } get value() { return privateDetails.get(this).values[0]; } type() { return this.value.type(); } }, 'CSSMathInvert': class extends CSSMathValue { constructor(values) { super([1, arguments[0]], 'invert', 'calc', ' / '); } get value() { return privateDetails.get(this).values[1]; } type() { const details = privateDetails.get(this) // The type of a CSSUnitValue is the result of creating a type from its unit internal slot. return invertType(details.values[1].type()) } }, 'CSSMathMax': class extends CSSMathValue { constructor() { super(arguments, 'max'); } }, 'CSSMathMin': class extends CSSMathValue { constructor() { super(arguments, 'min'); } } }; if (!window.CSS) { if (!Reflect.defineProperty(window, 'CSS', { value: {} })) throw Error(`Error installing CSSOM support`); } if (!window.CSSUnitValue) { [ 'number', 'percent', // Length units 'em', 'ex', 'px', 'cm', 'mm', 'in', 'pt', 'pc', // Picas 'Q', // Quarter millimeter 'vw', 'vh', 'vmin', 'vmax', 'rems', "ch", // Angle units 'deg', 'rad', 'grad', 'turn', // Time units 'ms', 's', 'Hz', 'kHz', // Resolution 'dppx', 'dpi', 'dpcm', // Other units "fr" ].forEach((name) => { const fn = (value) => { return new CSSUnitValue(value, name); }; if (!Reflect.defineProperty(CSS, name, { value: fn })) throw Error(`Error installing CSS.${name}`); }); } for (let [type, value] of Object.entries(cssOMTypes)) { if (type in window) continue; if (!Reflect.defineProperty(window, type, { value })) throw Error(`Error installing CSSOM support for ${type}`); } }