add step for entrance-transition
This commit is contained in:
+8
-4
@@ -36,9 +36,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"tdesign-mobile-vue": "^1.13.2",
|
||||
"timi-web": "link:E:/WebProject/timi-web",
|
||||
"vue": "3.5.26",
|
||||
"vue-router": "4.5.0"
|
||||
"timi-web": "link:E:/WebProject/timi-web"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.5.0",
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.13.5",
|
||||
@@ -63,7 +65,9 @@
|
||||
"vite-plugin-dts": "^4.5.4",
|
||||
"vite-plugin-vue-setup-extend": "^0.4.0",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"vue": "3.5.26",
|
||||
"vue-router": "4.5.0",
|
||||
"vite": "7.1.11",
|
||||
"vue-tsc": "^2.2.10"
|
||||
"vue-tsc": "^2.2.12"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+38
-17
@@ -14,12 +14,6 @@ importers:
|
||||
timi-web:
|
||||
specifier: link:E:/WebProject/timi-web
|
||||
version: link:../timi-web
|
||||
vue:
|
||||
specifier: 3.5.26
|
||||
version: 3.5.26(typescript@5.8.3)
|
||||
vue-router:
|
||||
specifier: 4.5.0
|
||||
version: 4.5.0(vue@3.5.26(typescript@5.8.3))
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^22.13.5
|
||||
@@ -90,9 +84,15 @@ importers:
|
||||
vite-plugin-vue-setup-extend:
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0(vite@7.1.11(@types/node@22.13.9)(less@4.3.0)(terser@5.46.0))
|
||||
vue:
|
||||
specifier: 3.5.26
|
||||
version: 3.5.26(typescript@5.8.3)
|
||||
vue-router:
|
||||
specifier: 4.5.0
|
||||
version: 4.5.0(vue@3.5.26(typescript@5.8.3))
|
||||
vue-tsc:
|
||||
specifier: ^2.2.10
|
||||
version: 2.2.10(typescript@5.8.3)
|
||||
specifier: ^2.2.12
|
||||
version: 2.2.12(typescript@5.8.3)
|
||||
|
||||
packages:
|
||||
|
||||
@@ -1309,12 +1309,21 @@ packages:
|
||||
'@volar/language-core@2.4.12':
|
||||
resolution: {integrity: sha512-RLrFdXEaQBWfSnYGVxvR2WrO6Bub0unkdHYIdC31HzIEqATIuuhRRzYu76iGPZ6OtA4Au1SnW0ZwIqPP217YhA==}
|
||||
|
||||
'@volar/language-core@2.4.15':
|
||||
resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==}
|
||||
|
||||
'@volar/source-map@2.4.12':
|
||||
resolution: {integrity: sha512-bUFIKvn2U0AWojOaqf63ER0N/iHIBYZPpNGogfLPQ68F5Eet6FnLlyho7BS0y2HJ1jFhSif7AcuTx1TqsCzRzw==}
|
||||
|
||||
'@volar/source-map@2.4.15':
|
||||
resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==}
|
||||
|
||||
'@volar/typescript@2.4.12':
|
||||
resolution: {integrity: sha512-HJB73OTJDgPc80K30wxi3if4fSsZZAOScbj2fcicMuOPoOkcf9NNAINb33o+DzhBdF9xTKC1gnPmIRDous5S0g==}
|
||||
|
||||
'@volar/typescript@2.4.15':
|
||||
resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==}
|
||||
|
||||
'@vue/babel-helper-vue-jsx-merge-props@1.4.0':
|
||||
resolution: {integrity: sha512-JkqXfCkUDp4PIlFdDQ0TdXoIejMtTHP67/pvxlgeY+u5k3LEdKuWZ3LK6xkxo52uDoABIVyRwqVkfLQJhk7VBA==}
|
||||
|
||||
@@ -1512,8 +1521,8 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
'@vue/language-core@2.2.10':
|
||||
resolution: {integrity: sha512-+yNoYx6XIKuAO8Mqh1vGytu8jkFEOH5C8iOv3i8Z/65A7x9iAOXA97Q+PqZ3nlm2lxf5rOJuIGI/wDtx/riNYw==}
|
||||
'@vue/language-core@2.2.12':
|
||||
resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
@@ -4772,8 +4781,8 @@ packages:
|
||||
vue-template-es2015-compiler@1.9.1:
|
||||
resolution: {integrity: sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==}
|
||||
|
||||
vue-tsc@2.2.10:
|
||||
resolution: {integrity: sha512-jWZ1xSaNbabEV3whpIDMbjVSVawjAyW+x1n3JeGQo7S0uv2n9F/JMgWW90tGWNFRKya4YwKMZgCtr0vRAM7DeQ==}
|
||||
vue-tsc@2.2.12:
|
||||
resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
typescript: '>=5.0.0'
|
||||
@@ -6272,14 +6281,26 @@ snapshots:
|
||||
dependencies:
|
||||
'@volar/source-map': 2.4.12
|
||||
|
||||
'@volar/language-core@2.4.15':
|
||||
dependencies:
|
||||
'@volar/source-map': 2.4.15
|
||||
|
||||
'@volar/source-map@2.4.12': {}
|
||||
|
||||
'@volar/source-map@2.4.15': {}
|
||||
|
||||
'@volar/typescript@2.4.12':
|
||||
dependencies:
|
||||
'@volar/language-core': 2.4.12
|
||||
path-browserify: 1.0.1
|
||||
vscode-uri: 3.1.0
|
||||
|
||||
'@volar/typescript@2.4.15':
|
||||
dependencies:
|
||||
'@volar/language-core': 2.4.15
|
||||
path-browserify: 1.0.1
|
||||
vscode-uri: 3.1.0
|
||||
|
||||
'@vue/babel-helper-vue-jsx-merge-props@1.4.0': {}
|
||||
|
||||
'@vue/babel-helper-vue-transform-on@1.3.0': {}
|
||||
@@ -6779,9 +6800,9 @@ snapshots:
|
||||
optionalDependencies:
|
||||
typescript: 5.8.3
|
||||
|
||||
'@vue/language-core@2.2.10(typescript@5.8.3)':
|
||||
'@vue/language-core@2.2.12(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@volar/language-core': 2.4.12
|
||||
'@volar/language-core': 2.4.15
|
||||
'@vue/compiler-dom': 3.5.41
|
||||
'@vue/compiler-vue2': 2.7.16
|
||||
'@vue/shared': 3.5.41
|
||||
@@ -10075,10 +10096,10 @@ snapshots:
|
||||
|
||||
vue-template-es2015-compiler@1.9.1: {}
|
||||
|
||||
vue-tsc@2.2.10(typescript@5.8.3):
|
||||
vue-tsc@2.2.12(typescript@5.8.3):
|
||||
dependencies:
|
||||
'@volar/typescript': 2.4.12
|
||||
'@vue/language-core': 2.2.10(typescript@5.8.3)
|
||||
'@volar/typescript': 2.4.15
|
||||
'@vue/language-core': 2.2.12(typescript@5.8.3)
|
||||
typescript: 5.8.3
|
||||
|
||||
vue@3.5.26(typescript@5.8.3):
|
||||
|
||||
@@ -12,9 +12,3 @@
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.entrance-fade-up-enter-active {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,31 @@
|
||||
import type { ObjectDirective } from "vue";
|
||||
|
||||
const ENTRANCE_DURATION = 400;
|
||||
const ENTRANCE_DELAY_STEP = 70;
|
||||
const ACTIVE_CLASS = "entrance-fade-up-enter-active";
|
||||
const FROM_CLASS = "entrance-fade-up-enter-from";
|
||||
const TO_CLASS = "entrance-fade-up-enter-to";
|
||||
const STATE_KEY = Symbol("entrance-transition-state");
|
||||
const AUTO_ELEMENTS = new WeakSet<HTMLElement>();
|
||||
|
||||
/** 入场动画指令支持的延迟值。 */
|
||||
export type EntranceTransitionValue = number | string | { delay?: number | string } | undefined;
|
||||
/** 入场动画指令支持的延迟配置;省略时按同级元素顺序自动错开。 */
|
||||
export type EntranceTransitionValue =
|
||||
| number
|
||||
| string
|
||||
| {
|
||||
/** 固定延迟;设置 step 时启用自动步进,step 优先。 */
|
||||
delay?: number | string;
|
||||
/** 自动步进间隔,单位为毫秒。 */
|
||||
step?: number | string;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
interface EntranceTransitionState {
|
||||
frameId?: number;
|
||||
timerId?: number;
|
||||
delay: number;
|
||||
step: number;
|
||||
auto: boolean;
|
||||
}
|
||||
|
||||
type EntranceTransitionElement = HTMLElement & {
|
||||
@@ -29,12 +42,36 @@ function setDelay(element: EntranceTransitionElement, delay: number): void {
|
||||
element.style.setProperty("--entrance-delay", `${delay}ms`);
|
||||
}
|
||||
|
||||
function cleanup(element: EntranceTransitionElement): void {
|
||||
const state = element[STATE_KEY];
|
||||
if (!state) {
|
||||
return;
|
||||
function isAuto(value: EntranceTransitionValue): boolean {
|
||||
return (
|
||||
value === undefined ||
|
||||
(typeof value === "object" && value !== null && value.step !== undefined)
|
||||
);
|
||||
}
|
||||
|
||||
function getAutoStep(value: EntranceTransitionValue): number {
|
||||
const rawStep =
|
||||
typeof value === "object" && value !== null
|
||||
? Number(value.step ?? ENTRANCE_DELAY_STEP)
|
||||
: ENTRANCE_DELAY_STEP;
|
||||
return Number.isFinite(rawStep) ? Math.max(0, rawStep) : ENTRANCE_DELAY_STEP;
|
||||
}
|
||||
|
||||
function getAutoDelay(element: EntranceTransitionElement, step: number): number {
|
||||
const parent = element.parentElement;
|
||||
if (!parent) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const siblings = Array.from(parent.children).filter(
|
||||
(sibling): sibling is HTMLElement => sibling instanceof HTMLElement && AUTO_ELEMENTS.has(sibling)
|
||||
);
|
||||
return Math.max(0, siblings.indexOf(element)) * step;
|
||||
}
|
||||
|
||||
function cleanup(element: EntranceTransitionElement, isUnmounting = false): void {
|
||||
const state = element[STATE_KEY];
|
||||
if (state) {
|
||||
if (state.frameId !== undefined) {
|
||||
window.cancelAnimationFrame(state.frameId);
|
||||
}
|
||||
@@ -47,6 +84,11 @@ function cleanup(element: EntranceTransitionElement): void {
|
||||
delete element[STATE_KEY];
|
||||
}
|
||||
|
||||
if (isUnmounting) {
|
||||
AUTO_ELEMENTS.delete(element);
|
||||
}
|
||||
}
|
||||
|
||||
/** 为元素添加一次性的渐入上移动画。 */
|
||||
export const VEntranceTransition: ObjectDirective<HTMLElement, EntranceTransitionValue> = {
|
||||
beforeMount(element, binding) {
|
||||
@@ -55,8 +97,15 @@ export const VEntranceTransition: ObjectDirective<HTMLElement, EntranceTransitio
|
||||
}
|
||||
|
||||
const target = element as EntranceTransitionElement;
|
||||
const delay = getDelay(binding.value);
|
||||
target[STATE_KEY] = { delay };
|
||||
const auto = isAuto(binding.value);
|
||||
const step = auto ? getAutoStep(binding.value) : ENTRANCE_DELAY_STEP;
|
||||
const delay = auto ? 0 : getDelay(binding.value);
|
||||
target[STATE_KEY] = { delay, step, auto };
|
||||
if (auto) {
|
||||
AUTO_ELEMENTS.add(target);
|
||||
} else {
|
||||
AUTO_ELEMENTS.delete(target);
|
||||
}
|
||||
setDelay(target, delay);
|
||||
target.classList.add(ACTIVE_CLASS, FROM_CLASS);
|
||||
},
|
||||
@@ -75,6 +124,10 @@ export const VEntranceTransition: ObjectDirective<HTMLElement, EntranceTransitio
|
||||
state.frameId = window.requestAnimationFrame(() => {
|
||||
state.frameId = window.requestAnimationFrame(() => {
|
||||
state.frameId = undefined;
|
||||
if (state.auto) {
|
||||
state.delay = getAutoDelay(target, state.step);
|
||||
setDelay(target, state.delay);
|
||||
}
|
||||
target.classList.remove(FROM_CLASS);
|
||||
target.classList.add(TO_CLASS);
|
||||
state.timerId = window.setTimeout(() => cleanup(target), ENTRANCE_DURATION + state.delay);
|
||||
@@ -88,18 +141,31 @@ export const VEntranceTransition: ObjectDirective<HTMLElement, EntranceTransitio
|
||||
}
|
||||
|
||||
const target = element as EntranceTransitionElement;
|
||||
const auto = isAuto(binding.value);
|
||||
if (auto) {
|
||||
AUTO_ELEMENTS.add(target);
|
||||
} else {
|
||||
AUTO_ELEMENTS.delete(target);
|
||||
}
|
||||
|
||||
const state = target[STATE_KEY];
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.auto = auto;
|
||||
state.step = auto ? getAutoStep(binding.value) : ENTRANCE_DELAY_STEP;
|
||||
if (state.auto) {
|
||||
state.delay = getAutoDelay(target, state.step);
|
||||
} else {
|
||||
state.delay = getDelay(binding.value);
|
||||
}
|
||||
setDelay(target, state.delay);
|
||||
},
|
||||
|
||||
beforeUnmount(element) {
|
||||
if (element instanceof HTMLElement) {
|
||||
cleanup(element as EntranceTransitionElement);
|
||||
cleanup(element as EntranceTransitionElement, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user