UNPKG

timered-counter

Version:

Make the value change more vivid and natural

195 lines 8.79 kB
import { __classPrivateFieldGet, __classPrivateFieldSet, __decorate } from "tslib"; import { zip } from 'd3-array'; import { property } from 'lit/decorators.js'; import { isArray, isObjectType } from 'remeda'; import { transitionDigit } from '../utils/transition-digit.js'; import { preprocessPartData, } from '../utils/preprocess-part-data.js'; import { parseJsonString } from '../utils/parse-json-string.js'; function toChar(value) { return String.fromCodePoint(value + 48); } function preprocessPartsOptions(options) { const digitToChar = {}; if (isArray(options.digitToChar)) { options.digitToChar.forEach((char, index) => { digitToChar[toChar(index)] = char; }); } else if (isObjectType(options.digitToChar)) { Object.entries(options.digitToChar).forEach(([key, value]) => { digitToChar[key] = value; }); } return { ...options, digitToChar, }; } export const CounterPartsMixin = (superClass) => { var _a, _CounterPartsMixinClass___partsOptions; class CounterPartsMixinClass extends superClass { constructor() { super(...arguments); _CounterPartsMixinClass___partsOptions.set(this, { ..._a.DEFAULT_PARTS_OPTIONS, }); // partsOptions: InnerPartsOptions = // CounterPartsMixinClass.DEFAULT_PARTS_OPTIONS; this.parts = []; this.oldParts = []; this.partPreprocessDataList = []; } /** * 这是 `usePartData` 的配置项. `usePartData` 被用于从数值的变化中生成用于滚动的数据. * 这里不会有太多解释, 因为它是一个底层的配置项. 你可以查看 `CounterPartsMixinClass` 的源码了解更多信息. */ get partsOptions() { return __classPrivateFieldGet(this, _CounterPartsMixinClass___partsOptions, "f"); } set partsOptions(value) { const old = __classPrivateFieldGet(this, _CounterPartsMixinClass___partsOptions, "f"); __classPrivateFieldSet(this, _CounterPartsMixinClass___partsOptions, preprocessPartsOptions({ ..._a.DEFAULT_PARTS_OPTIONS, ...value, }), "f"); this.requestUpdate('partsOptions', old); } sampling(from, to) { return transitionDigit(this.numberAdapter, this.numberAdapter.max(from, to), this.numberAdapter.min(from, to), this.partsOptions.sampleCount); } // eslint-disable-next-line class-methods-use-this sampleSplit(samples) { return [samples.slice()]; } sampleToString(value) { return this.numberAdapter.toString(value); } /** * 判断是否需要重新生成 parts 数据. * * {@link processPartData} 依赖 {@link partsOptions} 和 {@link value} 生成数据. 因此, 仅当依赖项变化时, 需要重新生成数据. * * @param changedProperties * @protected */ // eslint-disable-next-line class-methods-use-this shouldRebuildParts(changedProperties) { return (changedProperties.has('value') || changedProperties.has('partsOptions')); } willUpdate(changedProperties) { super.willUpdate(changedProperties); if (changedProperties.has('value')) { this.oldParts = this.parts; } if (this.shouldRebuildParts(changedProperties)) { this.parts = this.processPartData(); } if (changedProperties.has('value')) { this.partPreprocessDataList = preprocessPartData(this.direction, this.parts, this.oldDirection, this.oldParts); } /** * `value` 没变但 `direction` 变化时, 也要重新生成 `partPreprocessDataList`. */ // todo test events 和 animationOptions 冲突 // if ( // changedProperties.has('value') || // changedProperties.has('direction') // ) { // } } /** * process: * 1. 采样 * 2. 转换 * 3. 构造 */ processPartData() { const { decimalSeparator, digitToChar, minPlaces, fillChar, type } = this.partsOptions; const from = this.value; const to = this.oldValue; let _sampleToString = value => this.sampleToString(value).split(decimalSeparator); if (type === 'string') { _sampleToString = value => [this.sampleToString(value), '']; } const result = []; /** * 对 {@link from} 到 {@link to} 的范围采样. */ const tempParts = this.sampleSplit(this.sampling(from, to)); /** * 将时间部分的数字转换为用于滚动的字符串数组 * * headNumber: 最先显示的数字. 向下滚动时, 为滚动列表的最后一个数字, 向上滚动时相反. * tailNumber: 最后显示的数字. 向下滚动时, 为滚动列表的第一个数字, 向上滚动时相反. */ { const directionValue = this.numberAdapter.gt(from, to) ? 'down' : 'up'; for (let i = 0; i < tempParts.length; i++) { const partData = tempParts[i]; // const headNumber = // partData[directionValue === "down" ? partData.length - 1 : 0]; const tailNumber = partData[directionValue === 'down' ? 0 : partData.length - 1]; const [minIntegerPlaces = 1, minDecimalPlaces = 0] = minPlaces; const numberParts = _sampleToString(tailNumber); const integerPlaces = Math.max( // numberParts[0].length, this.stringAdapter.stringToChars(numberParts[0]).length, minIntegerPlaces); const decimalPlaces = Math.max( // numberParts[1]?.length ?? 0, this.stringAdapter.stringToChars(numberParts[1] ?? '').length, minDecimalPlaces); /** * 使用 zip 将二维矩阵, 旋转90度 */ const data = zip(...partData /** * 保证位数一致, 向前补零 */ .map(digitData => { const [integer = '', decimal = ''] = _sampleToString(digitData); const integerChars = this.stringAdapter.stringToChars(integer); const decimalChars = this.stringAdapter.stringToChars(decimal); const filledIntegerPlaces = Math.max(integerPlaces - integerChars.length, 0); const filledDecimalPlaces = Math.max(decimalPlaces - decimalChars.length, 0); let filledChars = [].concat(new Array(filledIntegerPlaces).fill(fillChar), integerChars); if (decimalPlaces > 0) { filledChars = filledChars.concat([decimalSeparator], decimalChars, new Array(filledDecimalPlaces).fill(fillChar)); } return filledChars; })) /** * 删除连续的重复项 */ .map((digitList, index, array) => ({ data: digitList .filter((digit, digitIndex, digitArray) => digitIndex === 0 || digit !== digitArray[digitIndex - 1]) .map(digit => digitToChar[digit] ?? digit), place: array.length - index, })); result.push({ digits: data, }); } } return result; } } _a = CounterPartsMixinClass, _CounterPartsMixinClass___partsOptions = new WeakMap(); CounterPartsMixinClass.DEFAULT_PARTS_OPTIONS = { sampleCount: 16, decimalSeparator: '.', fillChar: '0', minPlaces: [1, 0], digitToChar: {}, type: 'number', }; __decorate([ property({ type: Object, attribute: 'parts-options', converter: value => parseJsonString(value ?? '') ?? {}, noAccessor: true, }) ], CounterPartsMixinClass.prototype, "partsOptions", null); return CounterPartsMixinClass; }; //# sourceMappingURL=counter-parts.js.map