add step for entrance-transition
This commit is contained in:
+8
-4
@@ -36,9 +36,11 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tdesign-mobile-vue": "^1.13.2",
|
"tdesign-mobile-vue": "^1.13.2",
|
||||||
"timi-web": "link:E:/WebProject/timi-web",
|
"timi-web": "link:E:/WebProject/timi-web"
|
||||||
"vue": "3.5.26",
|
},
|
||||||
"vue-router": "4.5.0"
|
"peerDependencies": {
|
||||||
|
"vue": "^3.5.0",
|
||||||
|
"vue-router": "^4.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.13.5",
|
"@types/node": "^22.13.5",
|
||||||
@@ -63,7 +65,9 @@
|
|||||||
"vite-plugin-dts": "^4.5.4",
|
"vite-plugin-dts": "^4.5.4",
|
||||||
"vite-plugin-vue-setup-extend": "^0.4.0",
|
"vite-plugin-vue-setup-extend": "^0.4.0",
|
||||||
"@vue/tsconfig": "^0.7.0",
|
"@vue/tsconfig": "^0.7.0",
|
||||||
|
"vue": "3.5.26",
|
||||||
|
"vue-router": "4.5.0",
|
||||||
"vite": "7.1.11",
|
"vite": "7.1.11",
|
||||||
"vue-tsc": "^2.2.10"
|
"vue-tsc": "^2.2.12"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+38
-17
@@ -14,12 +14,6 @@ importers:
|
|||||||
timi-web:
|
timi-web:
|
||||||
specifier: link:E:/WebProject/timi-web
|
specifier: link:E:/WebProject/timi-web
|
||||||
version: link:../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:
|
devDependencies:
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^22.13.5
|
specifier: ^22.13.5
|
||||||
@@ -90,9 +84,15 @@ importers:
|
|||||||
vite-plugin-vue-setup-extend:
|
vite-plugin-vue-setup-extend:
|
||||||
specifier: ^0.4.0
|
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))
|
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:
|
vue-tsc:
|
||||||
specifier: ^2.2.10
|
specifier: ^2.2.12
|
||||||
version: 2.2.10(typescript@5.8.3)
|
version: 2.2.12(typescript@5.8.3)
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -1309,12 +1309,21 @@ packages:
|
|||||||
'@volar/language-core@2.4.12':
|
'@volar/language-core@2.4.12':
|
||||||
resolution: {integrity: sha512-RLrFdXEaQBWfSnYGVxvR2WrO6Bub0unkdHYIdC31HzIEqATIuuhRRzYu76iGPZ6OtA4Au1SnW0ZwIqPP217YhA==}
|
resolution: {integrity: sha512-RLrFdXEaQBWfSnYGVxvR2WrO6Bub0unkdHYIdC31HzIEqATIuuhRRzYu76iGPZ6OtA4Au1SnW0ZwIqPP217YhA==}
|
||||||
|
|
||||||
|
'@volar/language-core@2.4.15':
|
||||||
|
resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==}
|
||||||
|
|
||||||
'@volar/source-map@2.4.12':
|
'@volar/source-map@2.4.12':
|
||||||
resolution: {integrity: sha512-bUFIKvn2U0AWojOaqf63ER0N/iHIBYZPpNGogfLPQ68F5Eet6FnLlyho7BS0y2HJ1jFhSif7AcuTx1TqsCzRzw==}
|
resolution: {integrity: sha512-bUFIKvn2U0AWojOaqf63ER0N/iHIBYZPpNGogfLPQ68F5Eet6FnLlyho7BS0y2HJ1jFhSif7AcuTx1TqsCzRzw==}
|
||||||
|
|
||||||
|
'@volar/source-map@2.4.15':
|
||||||
|
resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==}
|
||||||
|
|
||||||
'@volar/typescript@2.4.12':
|
'@volar/typescript@2.4.12':
|
||||||
resolution: {integrity: sha512-HJB73OTJDgPc80K30wxi3if4fSsZZAOScbj2fcicMuOPoOkcf9NNAINb33o+DzhBdF9xTKC1gnPmIRDous5S0g==}
|
resolution: {integrity: sha512-HJB73OTJDgPc80K30wxi3if4fSsZZAOScbj2fcicMuOPoOkcf9NNAINb33o+DzhBdF9xTKC1gnPmIRDous5S0g==}
|
||||||
|
|
||||||
|
'@volar/typescript@2.4.15':
|
||||||
|
resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==}
|
||||||
|
|
||||||
'@vue/babel-helper-vue-jsx-merge-props@1.4.0':
|
'@vue/babel-helper-vue-jsx-merge-props@1.4.0':
|
||||||
resolution: {integrity: sha512-JkqXfCkUDp4PIlFdDQ0TdXoIejMtTHP67/pvxlgeY+u5k3LEdKuWZ3LK6xkxo52uDoABIVyRwqVkfLQJhk7VBA==}
|
resolution: {integrity: sha512-JkqXfCkUDp4PIlFdDQ0TdXoIejMtTHP67/pvxlgeY+u5k3LEdKuWZ3LK6xkxo52uDoABIVyRwqVkfLQJhk7VBA==}
|
||||||
|
|
||||||
@@ -1512,8 +1521,8 @@ packages:
|
|||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@vue/language-core@2.2.10':
|
'@vue/language-core@2.2.12':
|
||||||
resolution: {integrity: sha512-+yNoYx6XIKuAO8Mqh1vGytu8jkFEOH5C8iOv3i8Z/65A7x9iAOXA97Q+PqZ3nlm2lxf5rOJuIGI/wDtx/riNYw==}
|
resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
typescript: '*'
|
typescript: '*'
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
@@ -4772,8 +4781,8 @@ packages:
|
|||||||
vue-template-es2015-compiler@1.9.1:
|
vue-template-es2015-compiler@1.9.1:
|
||||||
resolution: {integrity: sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==}
|
resolution: {integrity: sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==}
|
||||||
|
|
||||||
vue-tsc@2.2.10:
|
vue-tsc@2.2.12:
|
||||||
resolution: {integrity: sha512-jWZ1xSaNbabEV3whpIDMbjVSVawjAyW+x1n3JeGQo7S0uv2n9F/JMgWW90tGWNFRKya4YwKMZgCtr0vRAM7DeQ==}
|
resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
typescript: '>=5.0.0'
|
typescript: '>=5.0.0'
|
||||||
@@ -6272,14 +6281,26 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@volar/source-map': 2.4.12
|
'@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.12': {}
|
||||||
|
|
||||||
|
'@volar/source-map@2.4.15': {}
|
||||||
|
|
||||||
'@volar/typescript@2.4.12':
|
'@volar/typescript@2.4.12':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@volar/language-core': 2.4.12
|
'@volar/language-core': 2.4.12
|
||||||
path-browserify: 1.0.1
|
path-browserify: 1.0.1
|
||||||
vscode-uri: 3.1.0
|
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-jsx-merge-props@1.4.0': {}
|
||||||
|
|
||||||
'@vue/babel-helper-vue-transform-on@1.3.0': {}
|
'@vue/babel-helper-vue-transform-on@1.3.0': {}
|
||||||
@@ -6779,9 +6800,9 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.8.3
|
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:
|
dependencies:
|
||||||
'@volar/language-core': 2.4.12
|
'@volar/language-core': 2.4.15
|
||||||
'@vue/compiler-dom': 3.5.41
|
'@vue/compiler-dom': 3.5.41
|
||||||
'@vue/compiler-vue2': 2.7.16
|
'@vue/compiler-vue2': 2.7.16
|
||||||
'@vue/shared': 3.5.41
|
'@vue/shared': 3.5.41
|
||||||
@@ -10075,10 +10096,10 @@ snapshots:
|
|||||||
|
|
||||||
vue-template-es2015-compiler@1.9.1: {}
|
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:
|
dependencies:
|
||||||
'@volar/typescript': 2.4.12
|
'@volar/typescript': 2.4.15
|
||||||
'@vue/language-core': 2.2.10(typescript@5.8.3)
|
'@vue/language-core': 2.2.12(typescript@5.8.3)
|
||||||
typescript: 5.8.3
|
typescript: 5.8.3
|
||||||
|
|
||||||
vue@3.5.26(typescript@5.8.3):
|
vue@3.5.26(typescript@5.8.3):
|
||||||
|
|||||||
@@ -12,9 +12,3 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translate3d(0, 0, 0);
|
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";
|
import type { ObjectDirective } from "vue";
|
||||||
|
|
||||||
const ENTRANCE_DURATION = 400;
|
const ENTRANCE_DURATION = 400;
|
||||||
|
const ENTRANCE_DELAY_STEP = 70;
|
||||||
const ACTIVE_CLASS = "entrance-fade-up-enter-active";
|
const ACTIVE_CLASS = "entrance-fade-up-enter-active";
|
||||||
const FROM_CLASS = "entrance-fade-up-enter-from";
|
const FROM_CLASS = "entrance-fade-up-enter-from";
|
||||||
const TO_CLASS = "entrance-fade-up-enter-to";
|
const TO_CLASS = "entrance-fade-up-enter-to";
|
||||||
const STATE_KEY = Symbol("entrance-transition-state");
|
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 {
|
interface EntranceTransitionState {
|
||||||
frameId?: number;
|
frameId?: number;
|
||||||
timerId?: number;
|
timerId?: number;
|
||||||
delay: number;
|
delay: number;
|
||||||
|
step: number;
|
||||||
|
auto: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type EntranceTransitionElement = HTMLElement & {
|
type EntranceTransitionElement = HTMLElement & {
|
||||||
@@ -29,12 +42,36 @@ function setDelay(element: EntranceTransitionElement, delay: number): void {
|
|||||||
element.style.setProperty("--entrance-delay", `${delay}ms`);
|
element.style.setProperty("--entrance-delay", `${delay}ms`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanup(element: EntranceTransitionElement): void {
|
function isAuto(value: EntranceTransitionValue): boolean {
|
||||||
const state = element[STATE_KEY];
|
return (
|
||||||
if (!state) {
|
value === undefined ||
|
||||||
return;
|
(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) {
|
if (state.frameId !== undefined) {
|
||||||
window.cancelAnimationFrame(state.frameId);
|
window.cancelAnimationFrame(state.frameId);
|
||||||
}
|
}
|
||||||
@@ -45,6 +82,11 @@ function cleanup(element: EntranceTransitionElement): void {
|
|||||||
element.classList.remove(ACTIVE_CLASS, FROM_CLASS, TO_CLASS);
|
element.classList.remove(ACTIVE_CLASS, FROM_CLASS, TO_CLASS);
|
||||||
element.style.removeProperty("--entrance-delay");
|
element.style.removeProperty("--entrance-delay");
|
||||||
delete element[STATE_KEY];
|
delete element[STATE_KEY];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUnmounting) {
|
||||||
|
AUTO_ELEMENTS.delete(element);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 为元素添加一次性的渐入上移动画。 */
|
/** 为元素添加一次性的渐入上移动画。 */
|
||||||
@@ -55,8 +97,15 @@ export const VEntranceTransition: ObjectDirective<HTMLElement, EntranceTransitio
|
|||||||
}
|
}
|
||||||
|
|
||||||
const target = element as EntranceTransitionElement;
|
const target = element as EntranceTransitionElement;
|
||||||
const delay = getDelay(binding.value);
|
const auto = isAuto(binding.value);
|
||||||
target[STATE_KEY] = { delay };
|
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);
|
setDelay(target, delay);
|
||||||
target.classList.add(ACTIVE_CLASS, FROM_CLASS);
|
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 = window.requestAnimationFrame(() => {
|
state.frameId = window.requestAnimationFrame(() => {
|
||||||
state.frameId = undefined;
|
state.frameId = undefined;
|
||||||
|
if (state.auto) {
|
||||||
|
state.delay = getAutoDelay(target, state.step);
|
||||||
|
setDelay(target, state.delay);
|
||||||
|
}
|
||||||
target.classList.remove(FROM_CLASS);
|
target.classList.remove(FROM_CLASS);
|
||||||
target.classList.add(TO_CLASS);
|
target.classList.add(TO_CLASS);
|
||||||
state.timerId = window.setTimeout(() => cleanup(target), ENTRANCE_DURATION + state.delay);
|
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 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];
|
const state = target[STATE_KEY];
|
||||||
if (!state) {
|
if (!state) {
|
||||||
return;
|
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);
|
state.delay = getDelay(binding.value);
|
||||||
|
}
|
||||||
setDelay(target, state.delay);
|
setDelay(target, state.delay);
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeUnmount(element) {
|
beforeUnmount(element) {
|
||||||
if (element instanceof HTMLElement) {
|
if (element instanceof HTMLElement) {
|
||||||
cleanup(element as EntranceTransitionElement);
|
cleanup(element as EntranceTransitionElement, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user