timered-counter
Version:
Make the value change more vivid and natural
228 lines • 9.13 kB
JavaScript
import { __decorate } from "tslib";
import { html, LitElement } from 'lit';
import { repeat } from 'lit/directives/repeat.js';
import { property, query } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js';
import { isNumber, values } from 'remeda';
import './roller-digit.js';
import { rollerStyles } from './styles.js';
import { graceDefineCustomElement } from '../../utils/grace-define-custom-element.js';
class RollerAnimationEvent extends Event {
}
export class TimeredCounterRoller extends LitElement {
constructor() {
super(...arguments);
this.color = '';
this.direction = 'up';
this.parts = [];
this.partPreprocessDataList = [];
this.animationOptions = [];
this.keyframes = [];
this.cellStyles = [];
this.digitStyles = [];
this.partStyles = [];
this.parentContainerRect = {};
this.__partDigitsColorStyles = [];
/**
* 记录一次更新中, 启动动画并结束的 <timered-counter-roller-digit> 元素个数.
*
* 在每次更新前重置为 0.
*
* @see {@link __handleDigitAnimationEnd}
* @private
*/
this.digitAnimateEndCount = 0;
/**
* 记录一次更新中, 需要启动动画的 <timered-counter-roller-digit> 元素总数.
*
* 在每次更新前重新计算.
* @private
*/
this.digitAnimatedCount = 0;
}
render() {
return html `<span
class="roller__prefix"
data-part-id="-1"
data-digit-id="0"
style=${styleMap((this.__partDigitsColorStyles?.[-1]?.[0] ?? {}))}
>
<slot part="prefix" name="prefix"></slot>
</span>
<span class="counter-parts">
${repeat(this.parts, (_, index) => index, (part, partIndex) => html `<span
part="part"
class="roller-part"
style=${styleMap((this.partStyles?.[partIndex] ?? {}))}
>${repeat(part.digits, (_, index) => `${part.digits.length - index}`, (digitInfo, digitIndex) => html `<timered-counter-roller-digit
exportparts="digit, cell"
part="digit"
class="roller-part__wrapper"
style=${styleMap((this.digitStyles?.[partIndex]?.[digitIndex] ??
{}))}
data-part-id="${partIndex}"
data-digit-id="${digitIndex}"
.digit=${digitInfo}
.preprocessData=${this.partPreprocessDataList[partIndex][digitIndex]}
.direction=${this.direction}
.textStyle=${this.__partDigitsColorStyles?.[partIndex]?.[digitIndex] ?? {}}
.cellStyle=${this.cellStyles[partIndex][digitIndex]}
.animationOptions=${this.animationOptions[partIndex][digitIndex]}
.keyframes=${this.keyframes[partIndex][digitIndex]}
-digit-animation-end=${this
.__handleDigitAnimationEnd}
></timered-counter-roller-digit>`)}${html `<span
class="roller-part__suffix"
data-part-id="${partIndex}"
data-digit-id="-1"
style=${styleMap((this.__partDigitsColorStyles?.[partIndex]?.[-1] ??
{}))}
><slot
part="part-suffix"
name=${`part-suffix-${partIndex}`}
></slot
></span>`}</span
> `)}</span
><span
class="roller__suffix"
data-part-id="-2"
data-digit-id="0"
style=${styleMap((this.__partDigitsColorStyles?.[-2]?.[0] ?? {}))}
><slot part="suffix" name="suffix"></slot
></span>`;
}
willUpdate(_changedProperties) {
super.willUpdate(_changedProperties);
if (_changedProperties.has('direction') ||
_changedProperties.has('parts')) {
this.digitAnimateEndCount = 0;
this.digitAnimatedCount = this.partPreprocessDataList
.flat()
.filter(({ animate }) => animate).length;
if (this.digitAnimatedCount > 0) {
this.__emitAnimationStart();
}
}
this.__partDigitsColorStyles = this.__generatePartDigitsColorStyles();
}
__handleDigitAnimationEnd() {
this.digitAnimateEndCount++;
if (this.digitAnimateEndCount < this.digitAnimatedCount)
return;
this.__emitAnimationEnd();
}
__emitAnimationStart() {
if (!this.isConnected)
return;
this.dispatchEvent(new RollerAnimationEvent('roller-animation-start'));
}
__emitAnimationEnd() {
if (!this.isConnected)
return;
this.dispatchEvent(new RollerAnimationEvent('roller-animation-end'));
}
__generatePartDigitsColorStyles() {
const result = [];
const containerRect = this.parentContainerRect;
const {
// parts,
// oldParts,
prefixContainer, suffixContainer, color, } = this;
const partDigitElements = Array.from(this.shadowRoot?.querySelectorAll('[data-part-id]').values() ?? []);
/**
* 当某次更新**将**会导致 DOM 宽高发生变化时(如: 滚动数字的位数增加/减少会导致宽度变化),
* 需要跳过这次更新然后等待 DOM 宽高变化完成才能更新(宽高变化后将通过 {@link containerRect} 触发).
*/
if (!containerRect) {
return result;
}
/**
* 比较 {@link data} 前后不同判断是否导致 DOM 宽度会发生变化.
*/
// if (parts.length !== oldParts.length) {
// this.requestUpdate();
// return result;
// }
// for (let i = 0; i < parts.length; i++) {
// if (parts[i].digits.length !== oldParts[i].digits.length) {
// this.requestUpdate();
// return result;
// }
// }
if (prefixContainer)
partDigitElements[-1] = prefixContainer;
if (suffixContainer)
partDigitElements[-2] = suffixContainer;
for (const el of values(partDigitElements)) {
const partId = Number.parseInt(el.dataset.partId ?? '-1', 10);
const digitId = Number.parseInt(el.dataset.digitId ?? '-1', 10);
if (!isNumber(partId) || !isNumber(digitId)) {
throw new Error('The data-part-id and data-digit-id attributes are required.');
}
if (!result[partId])
result[partId] = [];
if (CSS.supports('color', color)) {
result[partId][digitId] = { color };
}
else if (CSS.supports('background-image', color)) {
result[partId][digitId] = {
backgroundImage: color,
backgroundClip: 'text',
backgroundSize: `${this.offsetWidth}px ${this.offsetHeight}px`,
backgroundPositionX: `${-el.offsetLeft}px`,
backgroundPositionY: `${-el.offsetTop}px`,
backgroundRepeat: 'no-repeat',
color: 'transparent',
// @ts-ignore
'-webkit-text-fill-color': 'transparent',
};
}
else {
result[partId][digitId] = {};
// todo error event
// eslint-disable-next-line no-console
console.warn(new Error('The color property is not supported.'));
}
}
return result;
}
}
TimeredCounterRoller.styles = [rollerStyles];
__decorate([
property({ type: String })
], TimeredCounterRoller.prototype, "color", void 0);
__decorate([
property({ type: String })
], TimeredCounterRoller.prototype, "direction", void 0);
__decorate([
property({ type: Array })
], TimeredCounterRoller.prototype, "parts", void 0);
__decorate([
property({ type: Array })
], TimeredCounterRoller.prototype, "partPreprocessDataList", void 0);
__decorate([
property({ type: Array })
], TimeredCounterRoller.prototype, "animationOptions", void 0);
__decorate([
property({ type: Array })
], TimeredCounterRoller.prototype, "keyframes", void 0);
__decorate([
property({ type: Array })
], TimeredCounterRoller.prototype, "cellStyles", void 0);
__decorate([
property({ type: Array })
], TimeredCounterRoller.prototype, "digitStyles", void 0);
__decorate([
property({ type: Array })
], TimeredCounterRoller.prototype, "partStyles", void 0);
__decorate([
property({ type: Object })
], TimeredCounterRoller.prototype, "parentContainerRect", void 0);
__decorate([
query('.roller__prefix')
], TimeredCounterRoller.prototype, "prefixContainer", void 0);
__decorate([
query('.roller__suffix')
], TimeredCounterRoller.prototype, "suffixContainer", void 0);
graceDefineCustomElement('timered-counter-roller', TimeredCounterRoller);
//# sourceMappingURL=roller.js.map