vueデュアルオープンアニメーションを実現
必要:
前のページが離れるとダブルドアアニメーションがあり、後のページはアニメーションをスケールする効果があります.ダブルドアアニメーションをよく理解していない方は、pptのドアアニメーションを参考にしてください
バージョン反復
第1反復
設計の最初のアイデアは、親要素を作成し、2つのサブ要素を追加し、2つのサブ要素にvueアニメーションを設定することです.
インプリメンテーション
classを動的に変更した後、parent.vueコンポーネントと他のコンポーネントに切り替えるアニメーションが有効になります.
存在する問題はparent.vueコンポーネントが他のコンポーネントに切り替える場合、2番目のコンポーネントはルーティングの切り替えがこの時点で既に現れるためparent.vueコンポーネントは、2番目のコンポーネントの下にあります.実際のプロジェクト開発では,我々のparentコンポーネントのレイアウトは実際には全体であり,左右に分かれてレイアウトされることはない.問題を解決することを考えるのは、コンポーネントに位置決め属性を設定して解決することにほかならない.
問題2の解決策:
プロトタイプのpngピクチャを使用して、離れるときにピクチャにアニメーションを設定します.しかし、この案の問題は、デュアルオープンアニメーションを実現するには、左と右を半分ずつ配置する2回の画像を使用する必要があります.これにより、本当に鶏肋大画面のデータがリアルタイムで更新され、parentコンポーネントを離れたときに画像を切り取ることはできません.これにより、このコンポーネントを離れる際のデータのリアルタイム性を保証するだけでなく、canvasの方法でスクリーンショットを左右にそれぞれ配置することができる.
ソリューションは、第2のバージョンの問題を解決するために第2のソリューションを採用します.parentコンポーネントから離れるときにシリーズの動作を行う以上、ナビゲーションガードで処理してもいいです.
3回目の反復
このバージョンではhtml 2 canvasプラグインを使用してスクリーンショットの機能を実現し、canvasのgetImageDataおよびputImageDataメソッドと組み合わせてcanvasのスクリーンショットおよび保存を完了します.具体的な方法の紹介は参照できますhttps://www.w3school.com.cn/html5/canvas_getimagedata.asp
canvasを作成する際に注意すべきは、次のとおりです.デバイスの画素のため、canvasの属性を設定する場合、canvasのheightおよびwidthだけでなくcanvasも設定する.style.height/width.canvasを設定しないとstyle.height/widthでは、スクリーンショット前と作成したcanvasの画素比が一致しないという問題が発生します.canvas.style.height/widthはcanvasのheight/widthでwindowで割ったはずです.devicePixelRatioが得たものです.
注意:App.vueは変わらない
存在する問題ナビゲーションガードで一連の操作が完了するとnext()が次のコンポーネントに移行し、前後の2つのアニメーションが一緒に行われない.
この問題に対して,getImageDataメソッドを同期化することで解決できるかどうか考えられる.答えはだめです.同期の実行結果は現在と同じで、アニメーションの実行が完了してからnext が実行されます.
next()を先に実行してgetImageDataメソッドを実行しますか?答えは依然として否定的であり、next()を実行した後、parentコンポーネントから離れたことを意味し、parentコンポーネントはこの時点で破棄された.これは、再実行アニメーションがparentコンポーネントを取得できない要素であり、意味のないである.
getImageDataメソッドコード冗長性;
ソリューション問題1について、transitionのjsフックを用いることで を実現することが考えられる.問題2に対して、同じ論理のコードを から抽出する.
4回目の反復
上記バージョンは、離れたときに実行するアニメーションをindexに配置する.jsファイルでは、canvasを作成し、canvasにアニメーションを設定する方法を抽出し、コードの冗長性を回避します.
注目すべきは、 done()実行代表がparentコンポーネントを離れた場合、parentコンポーネントのアニメーションは実現されません.したがってdone()を非同期で実行する必要がある.また、アニメーションの実行待ち時間はアニメーションの実行時間より少し長くかかります.そうしないと、アニメーションがまだ実行されていないcanvasが消えてしまいます.
存在する問題 parentコンポーネントと他のコンポーネントをすばやく切り替えると、parentコンポーネントのアニメーション時間が長いため、以前のアニメーションが完了していないため、重複する問題が発生します. parentコンポーネントから他のコンポーネントにすばやく切り替えてparentコンポーネントを戻すと、アニメーションが完了せず、parentコンポーネントとアニメーションが重なるという問題もあります.
ソリューション問題1について、解決策は以下の通りである. は外層でApp.vueコンポーネントが$routeを傍受する場合from.nameがparentであればrouter-viewにparentというclass名 を付ける parentコンポーネントがgetImageDataメソッドを実行する前に、現在のdocumentに存在するclassがparent要素の個数であると判断し、存在する数が1より大きい場合はdone()を直接実行し、returnを実行する.
問題2について、解決策は以下の通りである. は外層でApp.vueコンポーネントが$routeを傍受する場合、to.nameがparentの場合、現在存在するclassがv-leave-toの要素の数であると判断し、存在する場合はその要素を除去する.
5回目の反復
実践結果
実践の中で発見して、問題の2を解決する時、問題は同時に解決して、だから私達はただ問題の2の解決策を実現するだけでいいです.
現存する問題
parentコンポーネントから他のコンポーネントに切り替えると、作成したcanvasが位置決めされているため、canvasが点滅します.
前のページが離れるとダブルドアアニメーションがあり、後のページはアニメーションをスケールする効果があります.ダブルドアアニメーションをよく理解していない方は、pptのドアアニメーションを参考にしてください
バージョン反復
第1反復
設計の最初のアイデアは、親要素を作成し、2つのサブ要素を追加し、2つのサブ要素にvueアニメーションを設定することです.
インプリメンテーション
// parent.vue
//APP.vue
export default {
data() {
return {
name: "parant",
};
},
watch: {
$route(to, from) {
// $route to from , name
if (from.name === "parent") {
this.name = "parent";
} else {
this.name = "index";
}
}
}
};
<style lang="less">
.parent-enter-active {
animation: parent-in 3.5s;
position: absolute;
top: 0;
}
.parent-leave-active {
animation: parent-in 3.5s reverse;
}
@keyframes parent-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
</style>
</code></pre>
<p> parent.vue - App.vue transition css 。</p>
<p><strong> </strong><br> , App.vue transition parent.vue 。</p>
<p><strong> </strong><br> , App.vue transition , router-view class 。 。</p>
<h2> </h2>
<pre><code>//App.vue
<template>
<div id="app">
<div id="nav">
<router-link to="/parent">parent</router-link> |
<router-link to="/about">About</router-link> |
<router-link to="/multi-element">Multielement</router-link> |
<router-link to="/list">list</router-link>
</div>
<router-view :class="{ view: isView }" ref="view" />
</div>
</template>
<script>
export default {
data() {
return {
isView: false
};
},
watch: {
$route(to, from) {
if (from.name === "parent") {
this.isView = true;
} else {
this.isView = false;
}
}
}
};
classを動的に変更した後、parent.vueコンポーネントと他のコンポーネントに切り替えるアニメーションが有効になります.
存在する問題はparent.vueコンポーネントが他のコンポーネントに切り替える場合、2番目のコンポーネントはルーティングの切り替えがこの時点で既に現れるためparent.vueコンポーネントは、2番目のコンポーネントの下にあります.実際のプロジェクト開発では,我々のparentコンポーネントのレイアウトは実際には全体であり,左右に分かれてレイアウトされることはない.問題を解決することを考えるのは、コンポーネントに位置決め属性を設定して解決することにほかならない.
問題2の解決策:
プロトタイプのpngピクチャを使用して、離れるときにピクチャにアニメーションを設定します.しかし、この案の問題は、デュアルオープンアニメーションを実現するには、左と右を半分ずつ配置する2回の画像を使用する必要があります.これにより、本当に鶏肋大画面のデータがリアルタイムで更新され、parentコンポーネントを離れたときに画像を切り取ることはできません.これにより、このコンポーネントを離れる際のデータのリアルタイム性を保証するだけでなく、canvasの方法でスクリーンショットを左右にそれぞれ配置することができる.
ソリューションは、第2のバージョンの問題を解決するために第2のソリューションを採用します.parentコンポーネントから離れるときにシリーズの動作を行う以上、ナビゲーションガードで処理してもいいです.
3回目の反復
//parent.vue
import echarts from "echarts";
import html2canvas from "html2canvas";
export default {
data() {
return {
transitionName: "slide-left",
options: {
xAxis: {
type: "category",
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
},
yAxis: {
type: "value"
},
series: [
{
data: [120, 200, 150, 80, 70, 110, 130],
type: "bar",
showBackground: true,
backgroundStyle: {
color: "rgba(220, 220, 220, 0.8)"
}
}
]
}
};
},
// transition
beforeRouteLeave(to, from, next) {
this.getImageData();
setTimeout(() => {
next();
}, 3000);
},
methods: {
getImageData() {
html2canvas(this.$refs["door"]).then(canvas => {
this.$refs["door"].style.display = "none";
//
const canvasWidth = +canvas.getAttribute("width");
const canvasHeight = +canvas.getAttribute("height");
// canvas
const styleHeight = `${canvasHeight / window.devicePixelRatio}px`;
const styleWidth = `${canvasWidth / 2 / window.devicePixelRatio}px`;
const leftCanvas = document.createElement("canvas");
//
leftCanvas.height = canvasHeight;
leftCanvas.width = canvasWidth / 2;
leftCanvas.style.height = styleHeight;
leftCanvas.style.width = styleWidth;
leftCanvas.style.top = "60px";
leftCanvas.style.position = "absolute";
leftCanvas.style.left = "0";
this.$refs["parent"].appendChild(leftCanvas);
const rightCanvas = document.createElement("canvas");
//
rightCanvas.height = canvasHeight;
rightCanvas.width = canvasWidth / 2;
rightCanvas.style.height = styleHeight;
rightCanvas.style.width = styleWidth;
rightCanvas.style.top = "60px";
rightCanvas.style.position = "absolute";
rightCanvas.style.right = "0";
this.$refs["parent"].appendChild(rightCanvas);
// canvas
const ctx = canvas.getContext("2d"); //
const leftImgData = ctx.getImageData(
0,
0,
canvasWidth / 2,
canvasHeight
);
const rightImgData = ctx.getImageData(
canvasWidth / 2,
0,
canvasWidth / 2,
canvasHeight
);
// canvas
const leftCtx = leftCanvas.getContext("2d");
const rightCtx = rightCanvas.getContext("2d");
// canvas
leftCtx.putImageData(leftImgData, 0, 0);
rightCtx.putImageData(rightImgData, 0, 0);
//
leftCanvas.style.transform = "rotateY(90deg)";
leftCanvas.style.transition = "transform 3s linear";
leftCanvas.style.transformStyle = "preserve-3d";
leftCanvas.style.transformOrigin = "left center";
rightCanvas.style.transform = "rotateY(-90deg)";
rightCanvas.style.transition = "transform 3s linear";
rightCanvas.style.transformStyle = "preserve-3d";
rightCanvas.style.transformOrigin = "right center";
});
}
},
created() {
this.$nextTick(() => {
let myChart = echarts.init(this.$refs.bar);
myChart.setOption(this.options);
});
}
};
このバージョンではhtml 2 canvasプラグインを使用してスクリーンショットの機能を実現し、canvasのgetImageDataおよびputImageDataメソッドと組み合わせてcanvasのスクリーンショットおよび保存を完了します.具体的な方法の紹介は参照できますhttps://www.w3school.com.cn/html5/canvas_getimagedata.asp
canvasを作成する際に注意すべきは、次のとおりです.
注意:App.vueは変わらない
存在する問題
この問題に対して,getImageDataメソッドを同期化することで解決できるかどうか考えられる.答えはだめです.同期の実行結果は現在と同じで、アニメーションの実行が完了してからnext
next()を先に実行してgetImageDataメソッドを実行しますか?答えは依然として否定的であり、next()を実行した後、parentコンポーネントから離れたことを意味し、parentコンポーネントはこの時点で破棄された.これは、再実行アニメーションがparentコンポーネントを取得できない要素であり、意味のないである.
ソリューション
4回目の反復
//parent.vue
import { getImageData } from "@/util/index.js";
import echarts from "echarts";
export default {
data() {
return {
transitionName: "slide-left",
options: {
xAxis: {
type: "category",
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
},
yAxis: {
type: "value"
},
series: [
{
data: [120, 200, 150, 80, 70, 110, 130],
type: "bar",
showBackground: true,
backgroundStyle: {
color: "rgba(220, 220, 220, 0.8)"
}
}
]
}
};
},
methods: {
leave(el, done) {
const basicTemp = this.$refs["door"];
getImageData(el, done, basicTemp);
}
},
created() {
this.$nextTick(() => {
let myChart = echarts.init(this.$refs.bar);
myChart.setOption(this.options);
});
}
};
//util/index.js
import html2canvas from "html2canvas";
function creatTempCanvas(el, height, width, leftFlag) {
const styleHeight = `${height / window.devicePixelRatio}px`;
const styleWidth = `${width / window.devicePixelRatio}px`;
const tempCanvas = document.createElement("canvas");
//
tempCanvas.height = height;
tempCanvas.width = width;
tempCanvas.style.height = styleHeight;
tempCanvas.style.width = styleWidth;
tempCanvas.style.top = "60px";
tempCanvas.style.position = "absolute";
if (leftFlag) {
tempCanvas.style.left = "0";
} else {
tempCanvas.style.right = "0";
}
el.appendChild(tempCanvas);
return tempCanvas;
}
function setTransition(dom, rotateAngle, time, origin) {
dom.style.transform = `rotateY(${rotateAngle})`;
dom.style.transition = `transform ${time} linear`;
dom.style.transformStyle = "preserve-3d";
dom.style.transformOrigin = `${origin}`;
}
export function getImageData(el, done, basicTemp) {
html2canvas(basicTemp).then(canvas => {
el.childNodes[0].style.display = "none";
//
const canvasWidth = +canvas.getAttribute("width");
const canvasHeight = +canvas.getAttribute("height");
// canvas
const leftCanvas = creatTempCanvas(el, canvasHeight, canvasWidth / 2, true);
const rightCanvas = creatTempCanvas(el, canvasHeight, canvasWidth / 2);
// canvas
const ctx = canvas.getContext("2d"); //
const leftImgData = ctx.getImageData(0, 0, canvasWidth / 2, canvasHeight);
const rightImgData = ctx.getImageData(
canvasWidth / 2,
0,
canvasWidth / 2,
canvasHeight
);
// canvas
const leftCtx = leftCanvas.getContext("2d");
const rightCtx = rightCanvas.getContext("2d");
// canvas
leftCtx.putImageData(leftImgData, 0, 0);
rightCtx.putImageData(rightImgData, 0, 0);
//
setTransition(leftCanvas, "-90deg", "3s", "left center");
setTransition(rightCanvas, "90deg", "3s", "right center");
setTimeout(() => {
done();
}, 5e3);
});
}
上記バージョンは、離れたときに実行するアニメーションをindexに配置する.jsファイルでは、canvasを作成し、canvasにアニメーションを設定する方法を抽出し、コードの冗長性を回避します.
注目すべきは、
存在する問題
ソリューション
5回目の反復
実践結果
実践の中で発見して、問題の2を解決する時、問題は同時に解決して、だから私達はただ問題の2の解決策を実現するだけでいいです.
//App.vue
export default {
data() {
return {
isView: false,
isParent: false
};
},
watch: {
$route(to, from) {
if (from.name === "parent") {
this.isView = true;
this.isParent = false;
} else {
this.isView = false;
this.isParent = true;
}
// to.name
if (to.name === "parent") {
const transitionDomList = document.getElementsByClassName("v-leave-to");
const { length } = transitionDomList;
if (length) {
transitionDomList[0].parentNode.removeChild(transitionDomList[0]);
}
}
}
},
methods: {}
};
現存する問題
parentコンポーネントから他のコンポーネントに切り替えると、作成したcanvasが位置決めされているため、canvasが点滅します.