mirror of https://github.com/sveltejs/svelte
parent
6ae268c878
commit
792a2215c3
@ -1,95 +1,96 @@
|
|||||||
import { append_empty_stylesheet, detach, get_root_for_style } from './dom';
|
import { append_empty_stylesheet, detach, get_root_for_style } from './dom';
|
||||||
import { raf } from './environment';
|
import { raf } from './environment';
|
||||||
|
|
||||||
interface StyleInformation {
|
|
||||||
stylesheet: CSSStyleSheet;
|
|
||||||
rules: Record<string, true>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we need to store the information for multiple documents because a Svelte application could also contain iframes
|
// we need to store the information for multiple documents because a Svelte application could also contain iframes
|
||||||
// https://github.com/sveltejs/svelte/issues/3624
|
// https://github.com/sveltejs/svelte/issues/3624
|
||||||
const managed_styles = new Map<Document | ShadowRoot, StyleInformation>();
|
const managed_styles = new Map();
|
||||||
let active = 0;
|
let active = 0;
|
||||||
|
|
||||||
// https://github.com/darkskyapp/string-hash/blob/master/index.js
|
// https://github.com/darkskyapp/string-hash/blob/master/index.js
|
||||||
function hash(str: string) {
|
/** @param {string} str
|
||||||
let hash = 5381;
|
* @returns {number}
|
||||||
let i = str.length;
|
*/
|
||||||
|
function hash(str) {
|
||||||
while (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i);
|
let hash = 5381;
|
||||||
return hash >>> 0;
|
let i = str.length;
|
||||||
|
while (i--)
|
||||||
|
hash = ((hash << 5) - hash) ^ str.charCodeAt(i);
|
||||||
|
return hash >>> 0;
|
||||||
}
|
}
|
||||||
|
/** @param {Document | ShadowRoot} doc
|
||||||
function create_style_information(
|
* @param {Element & ElementCSSInlineStyle} node
|
||||||
doc: Document | ShadowRoot,
|
* @returns {{ stylesheet: any; rules: {}; }}
|
||||||
node: Element & ElementCSSInlineStyle
|
*/
|
||||||
) {
|
function create_style_information(doc, node) {
|
||||||
const info = { stylesheet: append_empty_stylesheet(node), rules: {} };
|
const info = { stylesheet: append_empty_stylesheet(node), rules: {} };
|
||||||
managed_styles.set(doc, info);
|
managed_styles.set(doc, info);
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
/** @param {Element & ElementCSSInlineStyle} node
|
||||||
export function create_rule(
|
* @param {number} a
|
||||||
node: Element & ElementCSSInlineStyle,
|
* @param {number} b
|
||||||
a: number,
|
* @param {number} duration
|
||||||
b: number,
|
* @param {number} delay
|
||||||
duration: number,
|
* @param {(t: number) => number} ease
|
||||||
delay: number,
|
* @param {(t: number, u: number) => string} fn
|
||||||
ease: (t: number) => number,
|
* @param {number} uid
|
||||||
fn: (t: number, u: number) => string,
|
* @returns {string}
|
||||||
uid: number = 0
|
*/
|
||||||
) {
|
export function create_rule(node, a, b, duration, delay, ease, fn, uid = 0) {
|
||||||
const step = 16.666 / duration;
|
const step = 16.666 / duration;
|
||||||
let keyframes = '{\n';
|
let keyframes = '{\n';
|
||||||
|
for (let p = 0; p <= 1; p += step) {
|
||||||
for (let p = 0; p <= 1; p += step) {
|
const t = a + (b - a) * ease(p);
|
||||||
const t = a + (b - a) * ease(p);
|
keyframes += p * 100 + `%{${fn(t, 1 - t)}}\n`;
|
||||||
keyframes += p * 100 + `%{${fn(t, 1 - t)}}\n`;
|
}
|
||||||
}
|
const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`;
|
||||||
|
const name = `__svelte_${hash(rule)}_${uid}`;
|
||||||
const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`;
|
const doc = get_root_for_style(node);
|
||||||
const name = `__svelte_${hash(rule)}_${uid}`;
|
const { stylesheet, rules } = managed_styles.get(doc) || create_style_information(doc, node);
|
||||||
const doc = get_root_for_style(node);
|
if (!rules[name]) {
|
||||||
|
rules[name] = true;
|
||||||
const { stylesheet, rules } = managed_styles.get(doc) || create_style_information(doc, node);
|
stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length);
|
||||||
|
}
|
||||||
if (!rules[name]) {
|
const animation = node.style.animation || '';
|
||||||
rules[name] = true;
|
node.style.animation = `${animation ? `${animation}, ` : ''}${name} ${duration}ms linear ${delay}ms 1 both`;
|
||||||
stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length);
|
active += 1;
|
||||||
}
|
return name;
|
||||||
|
|
||||||
const animation = node.style.animation || '';
|
|
||||||
node.style.animation = `${
|
|
||||||
animation ? `${animation}, ` : ''
|
|
||||||
}${name} ${duration}ms linear ${delay}ms 1 both`;
|
|
||||||
|
|
||||||
active += 1;
|
|
||||||
return name;
|
|
||||||
}
|
}
|
||||||
|
/** @param {Element & ElementCSSInlineStyle} node
|
||||||
export function delete_rule(node: Element & ElementCSSInlineStyle, name?: string) {
|
* @param {string} name
|
||||||
const previous = (node.style.animation || '').split(', ');
|
* @returns {void}
|
||||||
const next = previous.filter(
|
*/
|
||||||
name
|
export function delete_rule(node, name) {
|
||||||
? (anim) => anim.indexOf(name) < 0 // remove specific animation
|
const previous = (node.style.animation || '').split(', ');
|
||||||
: (anim) => anim.indexOf('__svelte') === -1 // remove all Svelte animations
|
const next = previous.filter(name
|
||||||
);
|
? (anim) => anim.indexOf(name) < 0 // remove specific animation
|
||||||
const deleted = previous.length - next.length;
|
: (anim) => anim.indexOf('__svelte') === -1 // remove all Svelte animations
|
||||||
if (deleted) {
|
);
|
||||||
node.style.animation = next.join(', ');
|
const deleted = previous.length - next.length;
|
||||||
active -= deleted;
|
if (deleted) {
|
||||||
if (!active) clear_rules();
|
node.style.animation = next.join(', ');
|
||||||
}
|
active -= deleted;
|
||||||
|
if (!active)
|
||||||
|
clear_rules();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
/** @returns {void} */
|
||||||
export function clear_rules() {
|
export function clear_rules() {
|
||||||
raf(() => {
|
raf(() => {
|
||||||
if (active) return;
|
if (active)
|
||||||
managed_styles.forEach((info) => {
|
return;
|
||||||
const { ownerNode } = info.stylesheet;
|
managed_styles.forEach((info) => {
|
||||||
// there is no ownerNode if it runs on jsdom.
|
const { ownerNode } = info.stylesheet;
|
||||||
if (ownerNode) detach(ownerNode);
|
// there is no ownerNode if it runs on jsdom.
|
||||||
});
|
if (ownerNode)
|
||||||
managed_styles.clear();
|
detach(ownerNode);
|
||||||
});
|
});
|
||||||
|
managed_styles.clear();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** @typedef {Object} StyleInformation
|
||||||
|
* @property {CSSStyleSheet} stylesheet
|
||||||
|
* @property {Record<string,true>} rules
|
||||||
|
*/
|
||||||
Loading…
Reference in new issue