使ç¨è§å¾è¿æ¸¡ API
æ¬æä»ç»äºè§å¾è¿æ¸¡ API çå·¥ä½åçï¼å¦ä½å建è§å¾è¿æ¸¡ï¼å¦ä½èªå®ä¹è¿æ¸¡å¨ç»ï¼ä»¥åå¦ä½æä½æ´»å¨ç¶æçè§å¾è¿æ¸¡ãè¿æ¶µçäºå页åºç¨ç¨åºï¼SPAï¼ä¸ DOM ç¶ææ´æ°çè§å¾è¿æ¸¡ï¼ä»¥åå¨å¤é¡µåºç¨ç¨åºï¼MPAï¼ä¸çææ¡£ä¹é´ç导èªã
è§å¾è¿æ¸¡è¿ç¨
让æä»¬æ¥äºè§£ä¸ä¸è§å¾è¿æ¸¡çå·¥ä½åçï¼
-
è§å¾è¿æ¸¡è¢«è§¦åãå®å¦ä½æ§è¡åå³äºè§å¾è¿æ¸¡çç±»åï¼
- 对äºåææ¡£ï¼SPAï¼è¿æ¸¡ï¼éè¿å°è§¦åè§å¾æ´æ¹ DOM æ´æ°ç彿°ä½ä¸ºåè°å½æ°åæ°ä¼ éç»
document.startViewTransition()æ¹æ³æ¥è§¦åè§å¾è¿æ¸¡ã - 对äºè·¨ææ¡£ï¼MPAï¼è¿æ¸¡ï¼è§å¾è¿æ¸¡æ¯éè¿å¯å¨å°æ°ææ¡£çå¯¼èªæ¥è§¦åçã导èªçå½åææ¡£åç®æ ææ¡£éè¦åæºï¼å¹¶éè¿å¨å
¶ CSS ä¸å
å«
@view-transitionat è§åï¼å¹¶å°navigationæè¿°ç¬¦è®¾ç½®ä¸ºautoï¼æ¥éæ©æ·»å è§å¾è¿æ¸¡ã夿³¨ï¼æ´»å¨çè§å¾è¿æ¸¡å ·æå ³èç
ViewTransitionå®ä¾ï¼ä¾å¦ï¼å¨åææ¡£ï¼SPAï¼è¿æ¸¡çæ åµä¸ï¼ç±startViewTransition()è¿åï¼ãViewTransition对象å å«å¤ä¸ª Promiseï¼å è®¸ä½ è¿è¡ä»£ç 以ååºå°è¾¾è§å¾è¿æ¸¡è¿ç¨çä¸åé¨åãæå ³æ´å¤ä¿¡æ¯ï¼è¯·åé ä½¿ç¨ JavaScript æ§å¶è§å¾è¿æ¸¡ã
- 对äºåææ¡£ï¼SPAï¼è¿æ¸¡ï¼éè¿å°è§¦åè§å¾æ´æ¹ DOM æ´æ°ç彿°ä½ä¸ºåè°å½æ°åæ°ä¼ éç»
-
å¨å½åï¼æ§é¡µé¢ï¼è§å¾ä¸ï¼API æè·å£°æäº
view-transition-nameçå ç´ çå¿«ç §ã -
è§å¾æ´æ¹åçï¼
-
对äºåææ¡£ï¼SPAï¼è¿æ¸¡ï¼å°è°ç¨ä¼ éç»
startViewTransition()çåè°ï¼è¿ä¼å¯¼è´ DOM åçæ´æ¹ãå½åè°æåè¿è¡æ¶ï¼
ViewTransition.updateCallbackDonepromise å°å ç°ï¼å è®¸ä½ ååº DOM æ´æ°ã -
å¨è·¨ææ¡£ï¼MPAï¼è¿æ¸¡çæ åµä¸ï¼å¯¼èªåçå¨å½åææ¡£åç®æ ææ¡£ä¹é´ã
-
-
API å°æ°è§å¾ä¸çå¿«ç §æè·ä¸ºå®æ¶è¡¨ç¤ºçå½¢å¼ã
æ¤æ¶ï¼è§å¾è¿æ¸¡å³å°è¿è¡ï¼å¹¶ä¸
ViewTransition.readyPromise å ç°ãä¾å¦ï¼å è®¸ä½ éè¿è¿è¡èªå®ä¹ JavaScript å¨ç»è䏿¯é»è®¤å¨ç»æ¥ååºã -
æ§é¡µé¢å¿«ç §ä»¥âæ·¡åºâå¨ç»å½¢å¼æ¾ç¤ºï¼èæ°è§å¾å¿«ç §ä»¥âæ·¡å ¥âå½¢å¼åç°å¨ç»ææãé»è®¤æ åµä¸ï¼æ§è§å¾å¿«ç §çå¨ç»æææ¯
opacity屿§å¼ä» 1 å° 0ï¼èæ°è§å¾å¿«ç §çå¨ç»æææ¯opacity屿§å¼ä» 0 å° 1ï¼è¿ä¼å建ä¸ä¸ªäº¤åæ·¡åã -
å½è¿æ¸¡å¨ç»è¾¾å°å ¶ç»æç¶ææ¶ï¼
ViewTransition.finishedPromise å ç°ï¼ä»èå è®¸ä½ ååºååºã
夿³¨ï¼å¦æå¨ document.startViewTransition() è°ç¨æé´ï¼ææ¡£ç页é¢å¯è§æ§ç¶æå¤äº hidden ç¶æï¼ä¾å¦ï¼å¦æææ¡£è¢«çªå£é®æ¡ãæµè§å¨æå°åæå¦ä¸ä¸ªæµè§å¨é项å¡å¤äºæ´»å¨ç¶æï¼ï¼åä¼å®å
¨è·³è¿è§å¾è¿æ¸¡ã
è§å¾è¿æ¸¡ä¼ªå ç´ æ
为äºå¤çä¼ åºåä¼ å ¥è¿æ¸¡å¨ç»çåå»ºï¼æ¤ API æé äºä¸ä¸ªå ·æä»¥ä¸ç»æç伪å ç´ æ ï¼
::view-transition
ââ ::view-transition-group(root)
ââ ::view-transition-image-pair(root)
ââ ::view-transition-old(root)
ââ ::view-transition-new(root)
夿³¨ï¼æ¯ä¸ªè¢«æè·ç view-transition-name é½ä¼å建ä¸ä¸ª ::view-transition-group åæ ã
对äºåææ¡£ï¼SPAï¼è¿æ¸¡ï¼ä¼ªå ç´ æ å¨å½åææ¡£ä¸å¯ç¨ã对äºè·¨ææ¡£è¿æ¸¡ï¼MPAï¼ï¼ä¼ªå ç´ æ ä» å¨ç®æ ææ¡£ä¸å¯ç¨ã
æ ç»æä¸ææè¶£çé¨åå¦ä¸ï¼
-
::view-transitionæ¯è§å¾è¿æ¸¡é®ç½©å±çæ ¹ä¼ªå ç´ ï¼å®å 嫿æè§å¾è¿æ¸¡å¿«ç §ç»ï¼å¹¶ä½äºææå ¶ä»é¡µé¢å 容çé¡¶é¨ã -
::view-transition-groupå 彿¯ä¸ªè§å¾è¿æ¸¡å¿«ç §ç»ç容å¨ãrootåæ°æå®é»è®¤å¿«ç §ç»ââè§å¾è½¬æ¢å¨ç»å°åºç¨äºview-transition-name为rootçå¿«ç §ãé»è®¤æ åµä¸ï¼å®æ¯:rootå ç´ ï¼å 为é»è®¤çæµè§å¨æ ·å¼å®ä¹äºè¿ä¸ªï¼css:root { view-transition-name: root; }ä½è¯·æ³¨æï¼ç½é¡µçä½è å¯ä»¥éè¿åæ¶è®¾ç½®ä¸è¿°å 容并å¨å ¶ä»å ç´ ä¸è®¾ç½®
view-transition-name: rootæ¥æ´æ¹æ¤è®¾ç½®ã -
::view-transition-oldæåæ§ç页é¢å ç´ çéæå¿«ç §ï¼è::view-transition-newæåæ°ç页é¢å ç´ ç宿¶å¿«ç §ãè¿ä¸¤ä¸ªé项é½ä»¥ä¸<img>æ<video>ç¸åçæ¹å¼åç°ä¸ºæ¿æ¢å 容ï¼è¿æå³çå®ä»¬å¯ä»¥ä½¿ç¨æ¹ä¾¿ç屿§æ¥è®¾ç½®æ ·å¼ï¼å¦object-fitåobject-positionã
夿³¨ï¼å¯ä»¥éè¿å¨æ¯ä¸ªå
ç´ ä¸è®¾ç½®ä¸åç view-transition-name æ¥ä½¿ç¨ä¸åçèªå®ä¹è§å¾è¿æ¸¡å¨ç»æåä¸åç DOM å
ç´ ãå¨è¿ç§æ
åµä¸ï¼ä¼ä¸ºæ¯ä¸ªå
ç´ å建ä¸ä¸ª ::view-transition-groupãæå
³ç¤ºä¾ï¼è¯·åè§ä¸åå
ç´ çä¸åå¨ç»ã
夿³¨ï¼æ£å¦ä½ ç¨åå°çå°çï¼è¦èªå®ä¹ä¼ åºåä¼ å
¥å¨ç»ï¼ä½ éè¦å°å¨ç»å嫿å ::view-transition-old å ::view-transition-new 伪å
ç´ ã
åå»ºåºæ¬è§å¾è¿æ¸¡
æ¬è说æå¦ä½å¨ SPA å MPA æ åµä¸åå»ºåºæ¬è§å¾è¿æ¸¡ã
åºæ¬ SPA è§å¾è¿æ¸¡
ä¾å¦ï¼SPA å¯è½å
å«è·åæ°å
å®¹åæ´æ° DOM 以ååºæç§äºä»¶çåè½ï¼ä¾å¦åå»å¯¼èªé¾æ¥æä»æå¡å¨æ¨éæ´æ°ã卿们çè§å¾è¿æ¸¡ SPA æ¼ç¤ºä¸ï¼æä»¬å·²å°å
¶ç®å为 displayNewImage() 彿°ï¼è¯¥å½æ°æ ¹æ®åå»ç缩ç¥å¾æ¾ç¤ºæ°çå
¨å°ºå¯¸å¾åãæä»¬å°å
¶å°è£
å¨ä¸ä¸ª updateView() 彿°ä¸ï¼è¯¥å½æ°ä»
卿µè§å¨æ¯ææ¶è°ç¨è§å¾è¿æ¸¡ APIï¼
function updateView(event) {
// å¤çäºä»¶æ¯å¨ <a> è¿æ¯ <img> ä¸è§¦åçå·®å¼
const targetIdentifier = event.target.firstChild || event.target;
const displayNewImage = () => {
const mainSrc = `${targetIdentifier.src.split("_th.jpg")[0]}.jpg`;
galleryImg.src = mainSrc;
galleryCaption.textContent = targetIdentifier.alt;
};
// 䏿¯æè§å¾è¿æ¸¡çæµè§å¨çåéï¼
if (!document.startViewTransition) {
displayNewImage();
return;
}
// 使ç¨è§å¾è¿æ¸¡ï¼
const transition = document.startViewTransition(() => displayNewImage());
}
æ¤ä»£ç 足以å¤çæ¾ç¤ºå¾åä¹é´çè¿æ¸¡ãæ¯æçæµè§å¨ä¼å°ä»æ§å¾ååæ é¢å°æ°å¾ååæ é¢çæ´æ¹æ¾ç¤ºä¸ºå¹³æ»çäº¤åæ·¡åï¼å³é»è®¤è§å¾è¿æ¸¡ï¼ãå®ä»ç¶å¯ä»¥å¨ä¸æ¯æçæµè§å¨ä¸å·¥ä½ï¼ä½æ²¡ææ¼äº®çå¨ç»ã
åºæ¬ MPA è§å¾è¿æ¸¡
åå»ºè·¨ææ¡£ï¼MPAï¼è§å¾è¿æ¸¡æ¶ï¼è¯¥è¿ç¨çè³æ¯ SPA æ´ç®åãå 为è§å¾æ´æ°æ¯ç±è·¨ææ¡£ãåæºå¯¼èªè§¦åçï¼è䏿¯ç± JavaScript å¼åç DOM æ´æ¹è§¦åçï¼æä»¥ä¸éè¦ JavaScriptãè¦å¯ç¨åºæ¬ç MPA è§å¾è¿æ¸¡ï¼ä½ éè¦å¨ CSS ä¸ä¸ºå½åææ¡£åç®æ ææ¡£æå® @view-transition at è§å以鿩å¯ç¨ï¼å¦ä¸æç¤ºï¼
@view-transition {
navigation: auto;
}
æä»¬çè§å¾è¿æ¸¡ MPA æ¼ç¤ºå±ç¤ºäºè¿ä¸ªè§åçå®é åºç¨ï¼å¹¶è¿æ¼ç¤ºäºå¦ä½èªå®ä¹ä¼ åºåä¼ å ¥å¨ç»çè§å¾è¿æ¸¡ã
夿³¨ï¼ç®åï¼åªè½å¨åæºææ¡£ä¹é´å建 MPA è§å¾è¿æ¸¡ï¼ä½å¨å°æ¥ç宿½ä¸å¯è½ä¼æ¾å®½æ¤éå¶ã
èªå®ä¹å¨ç»
è§å¾è¿æ¸¡ä¼ªå ç´ åºç¨äºé»è®¤ç CSS å¨ç»ï¼è¯¦è§å ¶åè页é¢ï¼ã
å¦ä¸æè¿°ï¼å¤§å¤æ°å¤è§è¿æ¸¡é½å¸¦æé»è®¤çå¹³æ»äº¤åæ·¡åå¨ç»ãæä¸äºä¾å¤æ åµï¼
heightåwidthè¿æ¸¡åºç¨äºå¹³æ»ç缩æ¾å¨ç»ãpositionåtransformè¿æ¸¡åºç¨äºå¹³æ»çç§»å¨å¨ç»ã
ä½ å¯ä»¥ä½¿ç¨å¸¸è§ CSS 以任ä½ä½ æ³è¦çæ¹å¼ä¿®æ¹é»è®¤å¨ç»ââä½¿ç¨ ::view-transition-old å®ä½âæ¥æºâå¨ç»ï¼ä½¿ç¨ ::view-transition-new å®ä½âç®æ âå¨ç»ã
ä¾å¦ï¼è¦æ´æ¹ä¸¤è çé度ï¼
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 0.5s;
}
å»ºè®®ä½ å°è¿æ ·çæ ·å¼å®ä½å° ::view-transition-group()ï¼ä»¥å°å®ä»¬åºç¨äº ::view-transition-old() å ::view-transition-new()ãç±äºä¼ªå
ç´ å±æ¬¡ç»æåé»è®¤ç¨æ·ä»£çæ ·å¼ï¼æ ·å¼å°è¢«ä¸¤è
ç»§æ¿ãä¾å¦ï¼
::view-transition-group(root) {
animation-duration: 0.5s;
}
夿³¨ï¼è¿ä¹æ¯ä¿æ¤ä»£ç ç好鿩ââ::view-transition-group() ä¹å¯ä»¥å¨ç»åï¼å¹¶ä¸ group/image-pair 伪å
ç´ ä¸ old å new 伪å
ç´ çæç»æ¶é´å¯è½ä¼ææä¸åã
å¨è·¨ææ¡£ï¼MPAï¼è¿æ¸¡çæ åµä¸ï¼ä¼ªå ç´ éè¦å å«å¨ç®æ ææ¡£ä¸ï¼è§å¾è¿æ¸¡æè½æ£å¸¸å·¥ä½ãå¦æä½ æ³å¨ä¸¤ä¸ªæ¹åä¸ä½¿ç¨è§å¾è¿æ¸¡ï¼ä½ å½ç¶éè¦å¨ä¸¤ä¸ªæ¹åä¸é½å å«å®ã
æä»¬çè§å¾è¿æ¸¡ MPA æ¼ç¤ºå
æ¬ä¸è¿° CSSï¼ä½æ´è¿ä¸æ¥ï¼å®ä¹äºèªå®ä¹å¨ç»å¹¶å°å®ä»¬åºç¨äº ::view-transition-old(root) å ::view-transition-new(root) 伪å
ç´ ãç»ææ¯ï¼å¨è¿è¡å¯¼èªæ¶ï¼é»è®¤çäº¤åæ·¡åè¿æ¸¡è¢«æ¿æ¢æäºâå䏿»å¨âè¿æ¸¡ï¼
/* å建èªå®ä¹å¨ç» */
@keyframes move-out {
from {
transform: translateY(0%);
}
to {
transform: translateY(-100%);
}
}
@keyframes move-in {
from {
transform: translateY(100%);
}
to {
transform: translateY(0%);
}
}
/* å°èªå®ä¹å¨ç»åºç¨äºæ°æ§é¡µé¢ç¶æ */
::view-transition-old(root) {
animation: 0.4s ease-in both move-out;
}
::view-transition-new(root) {
animation: 0.4s ease-in both move-in;
}
ä¸åå ç´ çä¸åå¨ç»
é»è®¤æ
åµä¸ï¼å¨è§å¾æ´æ°æé´æ´æ¹çææä¸åå
ç´ é½ä½¿ç¨ç¸åçå¨ç»è¿è¡è¿æ¸¡ãå¦æä½ å¸ææäºå
ç´ çå¨ç»ææä¸é»è®¤ç root å¨ç»ä¸åï¼ä½ å¯ä»¥ä½¿ç¨ view-transition-name 屿§å°å®ä»¬åå¼ãä¾å¦ï¼å¨æä»¬çè§å¾è¿æ¸¡ SPA æ¼ç¤ºä¸ï¼<figcaption> å
ç´ è¢«èµäºäº figure-caption ç view-transition-nameï¼ä»¥ä¾¿å¨è§å¾è¿æ¸¡æ¹é¢å°å®ä»¬ä¸é¡µé¢çå
¶ä½é¨ååå¼ï¼
figcaption {
view-transition-name: figure-caption;
}
åºç¨æ¤ CSS åï¼çæç伪å ç´ æ ç°å¨å°å¦ä¸æç¤ºï¼
::view-transition
ââ ::view-transition-group(root)
â ââ ::view-transition-image-pair(root)
â ââ ::view-transition-old(root)
â ââ ::view-transition-new(root)
ââ ::view-transition-group(figure-caption)
ââ ::view-transition-image-pair(figure-caption)
ââ ::view-transition-old(figure-caption)
ââ ::view-transition-new(figure-caption)
第äºç»ä¼ªå
ç´ çåå¨å
许å°åç¬çè§å¾è¿æ¸¡æ ·å¼ä»
åºç¨äº <figcaption> å
ç´ ãä¸åçæ§è§å¾æè·åæ°è§å¾æè·å½¼æ¤åå¼å¤çã
夿³¨ï¼view-transition-name çå¼å¯ä»¥æ¯ä½ æ³è¦çä»»ä½å¼ï¼é¤äº none 以å¤âânone å¼æç¡®è¡¨ç¤ºå
ç´ ä¸ä¼åä¸è§å¾è¿æ¸¡ã
view-transition-name å¼ä¹å¿
é¡»æ¯å¯ä¸çãå¦æä¸¤ä¸ªæ¸²æçå
ç´ åæ¶å
·æç¸åç view-transition-nameï¼ViewTransition.ready å°æç»å¹¶è·³è¿è¿æ¸¡ã
以ä¸ä»£ç ä»
å°èªå®ä¹å¨ç»åºç¨äº <figcaption>ï¼
@keyframes grow-x {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
@keyframes shrink-x {
from {
transform: scaleX(1);
}
to {
transform: scaleX(0);
}
}
::view-transition-group(figure-caption) {
height: auto;
right: 0;
left: auto;
transform-origin: right center;
}
::view-transition-old(figure-caption) {
animation: 0.25s linear both shrink-x;
}
::view-transition-new(figure-caption) {
animation: 0.25s 0.25s linear both grow-x;
}
å¨è¿éï¼æä»¬å建äºä¸ä¸ªèªå®ä¹ç CSS å¨ç»ï¼å¹¶å°å
¶åºç¨äº ::view-transition-old(figure-caption) å ::view-transition-new(figure-caption) 伪å
ç´ ãæä»¬è¿ä¸ºè¿ä¸¤ä¸ªæ ·å¼æ·»å äºè®¸å¤å
¶ä»æ ·å¼ï¼ä»¥å°å®ä»¬ä¿æå¨åä¸ä¸ªä½ç½®ï¼å¹¶é²æ¢é»è®¤æ ·å¼å¹²æ°æä»¬çèªå®ä¹å¨ç»ã
夿³¨ï¼ä½ å¯ä»¥ä½¿ç¨ * ä½ä¸ºä¼ªå
ç´ ä¸çæ è¯ç¬¦ï¼ä»¥å®ä½ææå¿«ç
§ä¼ªå
ç´ ï¼æ 论å®ä»¬çåç§°å¦ä½ãä¾å¦ï¼
::view-transition-group(*) {
animation-duration: 2s;
}
使ç¨é»è®¤å¨ç»æ ·å¼
请注æï¼æä»¬è¿åç°äºå¦ä¸ä¸ªè¿æ¸¡é项ï¼å®æ¯ä¸è¿°é项æ´ç®åï¼å¹¶ä¸äº§çäºæ´å¥½çç»æãæä»¬æç»ç <figcaption> è§å¾è¿æ¸¡æç»çèµ·æ¥åè¿æ ·ï¼
figcaption {
view-transition-name: figure-caption;
}
::view-transition-group(figure-caption) {
height: 100%;
}
è¿ä¹æä»¥ææï¼æ¯å 为é»è®¤æ
åµä¸ï¼::view-transition-group 以平æ»çæ¯ä¾å¨æ°æ§è§å¾ä¹é´è½¬æ¢ width å heightãæä»¬åªéè¦å¨è¿ä¸¤ä¸ªç¶æä¸è®¾ç½®ä¸ä¸ªåºå®ç height æ¥ä½¿å
¶æ£å¸¸å·¥ä½ã
夿³¨ï¼ä½¿ç¨è§å¾è¿æ¸¡ API å®ç°å¹³æ»è¿æ¸¡å å«å ¶ä»å 个èªå®ä¹ç¤ºä¾ã
ä½¿ç¨ JavaScript æ§å¶è§å¾è¿æ¸¡
è§å¾è¿æ¸¡æä¸ä¸ªå
³èç ViewTransition 对象å®ä¾ï¼è¯¥å®ä¾å
å«å¤ä¸ª promise æåï¼å
è®¸ä½ è¿è¡ JavaScript 以ååºæè¾¾å°çè¿æ¸¡çä¸åç¶æãä¾å¦ï¼ViewTransition.ready å¨å建伪å
ç´ æ ä¸å¨ç»å³å°å¼å§æ¶å
ç°ï¼è ViewTransition.finished å¨å¨ç»å®æåå
ç°ï¼å¹¶ä¸æ°ç页é¢è§å¾å¯¹ç¨æ·å¯è§ä¸å
·æäº¤äºæ§ã
å¯ä»¥åè¿æ ·è®¿é® ViewTransitionï¼
- 对äºåææ¡£ï¼SPAï¼è¿æ¸¡ï¼
document.startViewTransition()æ¹æ³è¿åä¸è¿æ¸¡å ³èçViewTransition对象ã - 对äºè·¨ææ¡£ï¼MPAï¼è¿æ¸¡ï¼
- å½ææ¡£ç±äºå¯¼èªèå³å°å¸è½½æ¶ï¼å°è§¦å
pageswapäºä»¶ãå ¶äºä»¶å¯¹è±¡ï¼PageSwapEventï¼éè¿PageSwapEvent.viewTransition屿§æä¾å¯¹ViewTransition对象ç访é®ï¼ä»¥åéè¿PageSwapEvent.activationæä¾å¯¹NavigationActivationç访é®ï¼å ¶ä¸å å«å¯¼èªç±»å以åå½ååç®æ ææ¡£çåå²è®°å½æ¡ç®ã夿³¨ï¼å¦æå¯¼èªå¨éå®åé¾ä¸çä»»æä½ç½®å ·æè·¨æº URLï¼å
activation屿§è¿ånullã - 馿¬¡æ¸²æææ¡£æ¶ï¼ä¼è§¦å
pagerevealäºä»¶ï¼æ 论æ¯ä»ç½ç»å è½½æ°ææ¡£è¿æ¯æ¿æ´»ææ¡£ï¼ä»åé/åè¿ç¼åï¼bfcacheï¼æé¢æ¸²æï¼ï¼ãå ¶äºä»¶å¯¹è±¡ï¼PageRevealEventï¼éè¿PageRevealEvent.viewTransition屿§æä¾å¯¹ViewTransition对象ç访é®ã
- å½ææ¡£ç±äºå¯¼èªèå³å°å¸è½½æ¶ï¼å°è§¦å
让æä»¬çä¸äºç¤ºä¾ä»£ç æ¥å±ç¤ºå¦ä½ä½¿ç¨è¿äºåè½ã
ç± JavaScript æä¾æ¯æçèªå®ä¹åææ¡£ï¼SPAï¼è¿æ¸¡
ä»¥ä¸ JavaScript å¯ç¨äºå建ä»ç¨æ·å æ ä½ç½®ååºçå¾ªç¯æ¾ç¤ºçè§å¾è¿æ¸¡ï¼å¨ç»ç± Web å¨ç» API æä¾ã
// å卿åçç¹å»äºä»¶çäºä»¶å¯¹è±¡
let lastClick;
addEventListener("click", (event) => (lastClick = event));
function spaNavigate(data) {
// 对äºä¸æ¯ææ¤ API çæµè§å¨ï¼æ§è¡åéæä½ï¼
if (!document.startViewTransition) {
updateTheDOMSomehow(data);
return;
}
// è·åç¹å»ä½ç½®ï¼æåéå°å±å¹ä¸é´
const x = lastClick?.clientX ?? innerWidth / 2;
const y = lastClick?.clientY ?? innerHeight / 2;
// è·åå°æè¿çè§è½çè·ç¦»
const endRadius = Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y),
);
// åå»ºè¿æ¸¡ï¼
const transition = document.startViewTransition(() => {
updateTheDOMSomehow(data);
});
// çå¾
伪å
ç´ è¢«å建ï¼
transition.ready.then(() => {
// ä¸ºæ ¹çæ°è§å¾æ·»å å¨ç»ææ
document.documentElement.animate(
{
clipPath: [
`circle(0 at ${x}px ${y}px)`,
`circle(${endRadius}px at ${x}px ${y}px)`,
],
},
{
duration: 500,
easing: "ease-in",
// æå®è¦è¿è¡å¨ç»å¤çç伪å
ç´
pseudoElement: "::view-transition-new(root)",
},
);
});
}
æ¤å¨ç»è¿éè¦ä»¥ä¸ CSS æ¥å ³éé»è®¤ç CSS å¨ç»å¹¶é»æ¢æ°æ§è§å¾ç¶æä»¥ä»»ä½æ¹å¼æ··åï¼æ°ç¶æä¼å¨æ§ç¶æçé¡¶é¨âæ¦é¤âï¼è䏿¯è¿æ¸¡ï¼ï¼
::view-transition-image-pair(root) {
isolation: auto;
}
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
mix-blend-mode: normal;
display: block;
}
ç± JavaScript æä¾æ¯æçèªå®ä¹è·¨ææ¡£ï¼MPAï¼è¿æ¸¡
Chrome DevRel å¢éæåå表æ¼ç¤ºæä¾äºä¸ç»åºæ¬çå¢éé
ç½®æä»¶é¡µé¢ï¼å¹¶æ¼ç¤ºäºå¦ä½ä½¿ç¨ pageswap å pagereveal äºä»¶æ¥èªå®ä¹åºäºâæ¥æºâåâç®æ âURL çè·¨ææ¡£è§å¾è¿æ¸¡çä¼ åºåä¼ å
¥å¨ç»ã
pageswap äºä»¶ä¾¦å¬å¨å¦ä¸æç¤ºãè¿å°å¨åºç«é¡µé¢ä¸é¾æ¥å°ç¨æ·æ¡£æ¡é¡µé¢çå
ç´ ä¸è®¾ç½®è§å¾è¿æ¸¡åç§°ãä»ä¸»é¡µå¯¼èªå°é
ç½®æä»¶é¡µé¢æ¶ï¼ä»
ä¸ºå¨æ¯ç§æ
åµä¸åå»ç龿¥å
ç´ æä¾èªå®ä¹å¨ç»ã
window.addEventListener("pageswap", async (e) => {
// ä»
å½å卿´»å¨çè§å¾è¿æ¸¡æ¶ï¼æè¿è¡æ¤å½ä»¤
if (e.viewTransition) {
const currentUrl = e.activation.from?.url
? new URL(e.activation.from.url)
: null;
const targetUrl = new URL(e.activation.entry.url);
// ä»ä¸ªäººèµæé¡µé¢è½¬å°ä¸»é¡µ~>大å¾ååæ é¢å°±æ¯å
¶ä¸ä¹ä¸ï¼
if (isProfilePage(currentUrl) && isHomePage(targetUrl)) {
// å¨è¦è¿è¡å¨ç»å¤ççå
ç´ ä¸è®¾ç½® view-transition-name å¼
document.querySelector(`#detail main h1`).style.viewTransitionName =
"name";
document.querySelector(`#detail main img`).style.viewTransitionName =
"avatar";
// 卿æå¿«ç
§åå é¤ view-transition-nameï¼ä»¥åæ¢å BFCache 䏿ç»åå¨ç页é¢ç¶æè导è´çå½åå²çª
await e.viewTransition.finished;
document.querySelector(`#detail main h1`).style.viewTransitionName =
"none";
document.querySelector(`#detail main img`).style.viewTransitionName =
"none";
}
// è¿å
¥ä¸ªäººèµæé¡µé¢~>ç¹å»ç项ç®å°±æ¯é£äºï¼
if (isProfilePage(targetUrl)) {
const profile = extractProfileNameFromUrl(targetUrl);
// å¨è¦è¿è¡å¨ç»å¤ççå
ç´ ä¸è®¾ç½® view-transition-name å¼
document.querySelector(`#${profile} span`).style.viewTransitionName =
"name";
document.querySelector(`#${profile} img`).style.viewTransitionName =
"avatar";
// 卿æå¿«ç
§åå é¤ view-transition-nameï¼ä»è忢å BFCache 䏿ç»åå¨ç页é¢ç¶æè导è´çå½åå²çª
await e.viewTransition.finished;
document.querySelector(`#${profile} span`).style.viewTransitionName =
"none";
document.querySelector(`#${profile} img`).style.viewTransitionName =
"none";
}
}
});
夿³¨ï¼å¨æ¯ç§æ
åµä¸ï¼æä»¬å¨ææå¿«ç
§åå é¤ view-transition-name å¼ã妿æä»¬è®¾ç½®å®ä»¬ï¼å®ä»¬å°å¨å¯¼èªæ¶ä¿çå¨ bfcache ä¸ä¿åç页é¢ç¶æä¸ã妿éåæä¸åéæé®ï¼å被导èªåç页é¢ç pagereveal äºä»¶å¤çå¨å°å°è¯å¨ä¸åçå
ç´ ä¸è®¾ç½®ç¸åç view-transition-name å¼ã妿å¤ä¸ªå
ç´ è®¾ç½®äºç¸åç view-transition-name ï¼åè·³è¿è§å¾è¿æ¸¡ã
pagereveal äºä»¶ä¾¦å¬å¨å¦ä¸æç¤ºãè¿ä¸ pageswap äºä»¶ä¾¦å¬å¨ç工使¹å¼ç±»ä¼¼ï¼ä½è¯·è®°ä½ï¼è¿éæä»¬ä¸ºæ°é¡µé¢ä¸ç页é¢å
ç´ èªå®ä¹âç®æ âå¨ç»ã
window.addEventListener("pagereveal", async (e) => {
// å¦æâæ¥æºâåå²è®°å½æ¡ç®ä¸åå¨ï¼åè¿å
if (!navigation.activation.from) return;
// ä»
å½å卿´»å¨çè§å¾è¿æ¸¡æ¶ï¼æè¿è¡æ¤å½ä»¤
if (e.viewTransition) {
const fromUrl = new URL(navigation.activation.from.url);
const currentUrl = new URL(navigation.activation.entry.url);
// ä»ä¸ªäººèµæé¡µé¢è½¬å°ä¸»é¡µ~>å¨ç¸å
³å表项ä¸è®¾ç½® VT åç§°
if (isProfilePage(fromUrl) && isHomePage(currentUrl)) {
const profile = extractProfileNameFromUrl(fromUrl);
// å¨è¦è¿è¡å¨ç»å¤ççå
ç´ ä¸è®¾ç½® view-transition-name å¼
document.querySelector(`#${profile} span`).style.viewTransitionName =
"name";
document.querySelector(`#${profile} img`).style.viewTransitionName =
"avatar";
// 卿æå¿«ç
§åå é¤åç§°ï¼ä»¥ä¾¿æä»¬ä¸ºä¸ä¸æ¬¡å¯¼èªå好åå¤
await e.viewTransition.ready;
document.querySelector(`#${profile} span`).style.viewTransitionName =
"none";
document.querySelector(`#${profile} img`).style.viewTransitionName =
"none";
}
// 转å°ä¸ªäººèµæé¡µé¢~>å¨ä¸»æ é¢åå¾åä¸è®¾ç½® VT åç§°
if (isProfilePage(currentUrl)) {
// å¨è¦è¿è¡å¨ç»å¤ççå
ç´ ä¸è®¾ç½® view-transition-name å¼
document.querySelector(`#detail main h1`).style.viewTransitionName =
"name";
document.querySelector(`#detail main img`).style.viewTransitionName =
"avatar";
// 卿æå¿«ç
§åå é¤åç§°ï¼ä»¥ä¾¿æä»¬ä¸ºä¸ä¸æ¬¡å¯¼èªå好åå¤
await e.viewTransition.ready;
document.querySelector(`#detail main h1`).style.viewTransitionName =
"none";
document.querySelector(`#detail main img`).style.viewTransitionName =
"none";
}
}
});
稳å®é¡µé¢ç¶æä»¥ä½¿è·¨ææ¡£è¿æ¸¡ææä¸è´
å¨è¿è¡è·¨ææ¡£è¿æ¸¡ä¹åï¼ä½ æå¥½çå°é¡µé¢ç¶æç¨³å®ä¸æ¥ï¼ä¾é 渲æé»å¡æ¥ç¡®ä¿ï¼
- å 载并åºç¨å ³é®æ ·å¼ã
- å 载并è¿è¡å ³é®èæ¬ã
- å¯¹ç¨æ·é¡µé¢çåå§è§å¾å¯è§ç HTML 已解æï¼å æ¤å®å¯ä»¥ä¸è´å°åç°ã
é»è®¤æ
åµä¸ï¼æ ·å¼æ¯è¢«æ¸²æé»å¡çï¼å¹¶ä¸èæ¬å¯ä»¥éè¿ä½¿ç¨ blocking="render" 屿§æ¥è¢«æ¸²æé»å¡ã
è¦ç¡®ä¿åå§ HTML 已解æå¹¶å¨è¿æ¸¡å¨ç»è¿è¡ä¹åå§ç»ä¸è´å°åç°ï¼ä½ å¯ä»¥ä½¿ç¨ <link rel="expect">ã卿¤å
ç´ ä¸ï¼ä½ å°å
æ¬ä»¥ä¸å±æ§ï¼
rel="expect"è¡¨ç¤ºä½ æ³ç¨è¿ä¸ª<link>å ç´ å¨é¡µé¢ä¸æ¸²æä¸äº HTMLãhref="#element-id"æ¥è¡¨ç¤ºä½ æ³è¦æ¸²æçåçå ç´ ç IDãblocking="render"æ¥ä½¿æå®ç HTML å®ç°æ¸²æé»å¡ã
让æä»¬éè¿ä¸ä¸ªç¤ºä¾ HTML ææ¡£æ¥æ¢è®¨ä¸ä¸å®æ¯ä»ä¹æ ·åçï¼
<!doctype html>
<html lang="en">
<head>
<!-- é»è®¤æ
åµä¸ï¼è¿å°ä¼æ¯æ¸²æé»å¡ç -->
<link rel="stylesheet" href="style.css" />
<!-- å°å
³é®èæ¬æ 记为渲æé»å¡å°ç¡®ä¿å®ä»¬å¨æ¿æ´»è§å¾è¿æ¸¡ä¹åè¿è¡ -->
<script async href="layout.js" blocking="render"></script>
<!-- ä½¿ç¨ rel="expect" å blocking="render" ç¡®ä¿å¨æ¿æ´»è¿æ¸¡ä¹åï¼#lead-content å
ç´ å¯è§å¹¶è¢«å®å
¨è§£æ -->
<link rel="expect" href="#lead-content" blocking="render" />
</head>
<body>
<h1>页颿 é¢</h1>
<nav>...</nav>
<div id="lead-content">
<section id="first-section">第ä¸é¨å</section>
<section>第äºé¨å</section>
</div>
</body>
</html>
ç»ææ¯ï¼å¨è§£ææ½å¨å
容 <div> ä¹åï¼ææ¡£æ¸²æä¼è¢«é»æ¢ï¼ä»èç¡®ä¿è§å¾è¿æ¸¡çä¸è´æ§ã
ä½ è¿å¯ä»¥å¨ <link rel="expect"> å
ç´ ä¸æå® media 屿§ãä¾å¦ï¼å¨çªå±å¹è®¾å¤ä¸å è½½é¡µé¢æ¶ï¼ä½ å¯è½å¸æé»æ¢å¨æ¯å¨å®½å±è®¾å¤ä¸å è½½é¡µé¢æ¶å°çå
容ä¸åç°ãè¿æ¯æéççââå¨ç§»å¨è®¾å¤ä¸ï¼é¡µé¢é¦æ¬¡å è½½æ¶å¯è§çå
容æ¯å¨æ¡é¢ä¸è¦å°ã
è¿å¯ä»¥éè¿ä»¥ä¸ HTML æ¥å®ç°ï¼
<link
rel="expect"
href="#lead-content"
blocking="render"
media="screen and (min-width: 641px)" />
<link
rel="expect"
href="#first-section"
blocking="render"
media="screen and (max-width: 640px)" />