1. 程式人生 > 其它 >vue3-元件 動畫 過渡 複用

vue3-元件 動畫 過渡 複用

<!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>綜合練習:元件 過度&動畫 可複用性&組合 </title> <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.2.31/vue.global.js"></script> <style> #app { border: 1px solid red; width: 550px; height: 1500px; margin: 0 auto; padding: 0; text-align: center; }
#grandfather { border: 1px solid pink; margin-top: 100px; width: 550px; height: 500px; margin: 0 auto; padding: 0; text-align: center; }
#lzx { border: 1px solid purple; margin-top: 100px; width: 550px; height: 1000px; margin: 0 auto; padding: 0; text-align: center; }
#animation1 { border: 1px solid rgb(28, 14, 28); margin-top: 100px; width: 550px; height: 1000px; margin: 0 auto; padding: 0; text-align: center; }



.abc { margin: 0 auto; width: 150px; height: 150px; ">rgb(0, 0, 0); transition: all 1.5s; }




/* 進入之前 */ .sky-enter-from { width: 0; height: 0; ">rgb(228, 253, 6); transform: rotate(180deg); }
/* 進入過渡之中的狀態 */
.sky-enter-active { transition: all 1.5s ease; }
/* 進入完成的狀態 */
.sky-enter-to { width: 250px; height: 250px; ">rgb(238, 216, 216);
}
/* 離開之前 */
.sky-leave-from { width: 50px; height: 50px; ">yellow;
}
/* 離開過度之中的狀態 */ .sky-leave-avtive {
transition: all 2s ease-out; }
/* 離開完成之後的狀態 */ .sky-leave-to { transform: rotate(60deg); ">yellow; width: 0; height: 0; }
.aaa { width: 30px; height: 50px; ">#008000; }
.bbb { transition: all 5s ease; }
.ccc { width: 500px; height: 500px; ">red; }
.ddd { width: 300px; height: 500px; ">#333833; }
.fff { transition: all 2s ease; }
.hhh { width: 0px; height: 0px; ">rgb(19, 228, 141); }
/*多元素過度 */ #animation2 { border: 1px solid rgb(28, 14, 28); margin-top: 100px; width: 550px; height: 1000px; margin: 0 auto; padding: 0; text-align: center; }
.bac { width: 100px; height: 100px; ">red;
margin-left: 50px; }
.bba { width: 100px; height: 100px;   margin-left: 50px; }
.cba { width: 100px; height: 100px; ">rgb(126, 243, 16); margin-left: 50px; }
/* .mmm-leave-active { transition: all 2s ease; }
.mmm-leave-to { width: 0; height: 0; transform: rotate(30deg); } */
.leaveActive { transition: all 1s ease; }
.leaveTo { width: 0; height: 0; transform: rotate(30deg); opacity: 0; }
.enterActive2 { transition: all .7s ease; }
.enterFrom { width: 0; height: 0; ">green; opacity: 0; }
/* 多元素過度 */ #animation3 { border: 1px solid rgb(28, 14, 28); margin-top: 100px; width: 550px; height: 1000px; margin: 0 auto; padding: 0; text-align: center; }
.aa { width: 100px; height: 100px;   }
.bb { width: 100px; height: 100px;  
}
.enterFrom2 { opacity: 0; }
.enterActive3 { transition: all 2s ease; }
.leaveTo2 { opacity: 0;
}
.leaveActive2 { transition: all 1s ease; }
/* 多元素列表過度 */ #animation4 { border: 1px solid rgb(28, 14, 28); margin-top: 100px; width: 550px; height: 1000px; margin: 0 auto; padding: 0; text-align: center;
}
#animation4 button { float: left; padding: 0 10px; margin-right: 10px; }
.skyblue { float: left; margin-top: 100px; padding: 0 10px; }
.move { transition: 1s; }
/* 自定義指令 */ #directive1 { border: 1px solid rgb(28, 14, 28); margin-top: 100px; width: 550px; height: 1000px; margin: 0 auto; padding: 0; text-align: center; } </style> </head>
<body> <!-- 元件根元件 --> <div id="app"></div> <!-- provide + inject 實現多級傳值 --> <!-- 建立一個爺爺dom --> <div id="grandfather"> </div> <!-- vue3寫法--> <div id="lzx"> <div ref="div"></div> <span ref="span"></span> <input @click="go" type="button" value="go" /> <lzx></lzx> </div> <!-- 動畫基礎 --> <!-- style類名必須放在一個順序裡 --> <div id="animation1"> <input style="margin-bottom:50px" @click="ok=!ok" type="button" name="" value="go" id=""> <div>{{text}}</div> <div>{{content}}</div> <!-- 動態改變類名,可以更改動畫樣式 duration設定生效時間,包含進入時間,離開時間 --> <transition @before-enter="beforeEnter" @enter="Enter" @after-enter="afterEnter" name="sky" enter-from-class="aaa" esnter-active-class="bbb" enter-to-class="ccc" leave-from-class="ddd" leave-active-class="fff" leave-to-class="hhh" :duration="{enter:1000,leave:1000}"> <div v-if="ok" :class="{changeColr:ok}" class="abc"></div> </transition>
</div> <!-- 動畫:多元素過度 +過渡模式out-in +多元件過度 --> <div id="animation2"> <button @click="show++">go</button> <transition mode="out-in" enter-from-class="enterFrom" enter-active-class="enterActive2" leave-to-class="leaveTo" leave-active-class="leaveActive" name="mmm"> <!-- 多元素過度 --> <div class="bac" v-if="show==0"></div> <div class="bba" v-else-if="show==1"></div> <div class="cba" v-else-if="show==2"></div> </transition> <!-- 多元件過度 --> </div> <div id="animation3"> <input v-model="view" value="aa" type="radio" checked id="a" /> <label for="a">元件A</label> <input v-model="view" value="bb" type="radio" id="b" /> <label for="b">元件B</label> <transition mode="out-in" leave-to-class="leaveTo2" leave-class="leaveActive2" enter-from-class="enterFrom2" enter-active-class="enterActive3"> <!--多元件過度 --> <component :is="view"></component> </transition> </div> <!-- 多元素列表過度 transition-group +排序過渡--> <div id="animation4"> <button @click="add">add</button> <button @click="remove">remove</button> <button @click="change">change</button> <transition-group move-class="move" leave-active-class="enterActive2" leave-to-class="enterFrom2" enter-active-class="enterActive2" enter-from-class="enterFrom2"> <!-- <div :key="1" v-if="ok">1</div> <div :key="2" v-if="ok">2</div> --> <div class="skyblue" v-for="item in arr" :key="item"> {{item}} </div>
</transition-group> </div> <!-- 自定義元件 --> <div id="directive1"> <input type="text" value="" id="" v-focus> <input value="add" type="button" @click="num++" /> <p v-color="'red'"> {{num}}</p> </div>



</body> <script> // 二、自定義指令 //1、註冊一個全域性的自定義指令 const dir = Vue.createApp({ data() { return { num: 1 } }, }) // dir.directive('focus', { // mounted(el) { // el.focus() // } // }) //自動獲取焦點指令 //函式簡寫 //如果mounted與updata邏輯相同,可以簡寫,會同時在兩個生命週期觸發 dir.directive('focus', (el) => { el.focus() }) //設定樣式指令,動態指令引數 dir.directive('color', (el, binding) => { //通過等號傳遞指令 el.style.color = binding.value console.log(binding); }) dir.mount('#directive1') //vue3寫法 // const dir = Vue.createApp({ // data() { // return { // num: 1 // } // }, // directives: { // focus: { // mounted(el) { // console.log('mounted'); // el.focus() // }, // updated() { // console.log('updated'); // }, // } // } // }).mount('#directive1')





// 2、註冊一個區域性的自定義指令 //註冊一個區域性自定義指令 v-focus //指令的定義 //引數有: //el:傳入與指令繫結的元素,可以用來操作dom //bingding:一個物件,屬性有: //1、name:指令名稱 不包括字首v- //2、value:指令繫結值:例如v-focus=2;2既是value //3、oldValue:指令繫結前的一個值。在updata,componentUpdata鉤子可用 //4、expression:繫結值的表示式或變數名。例如:v-focus='1+1',expresion 的值是1+1 //5、arg:傳給指令的引數。例如v-focus:foo arg的值是foo //6、modifiers:一個包含修飾符的物件,例如:v-focus.foo.bar modifiers的值為{foo:true,bar:true} //7、vnode:vue編譯生成的虛擬節點 //8、oldVnode:上一個虛擬節點,在updata,componentUpdata鉤子可用 //只調用一次,指令與元素繫結時呼叫,可以定義第一次繫結元素時初始化動作 // bind: function () { // }, // // inserted鉤子函式,被繫結元素在插入父節點時使用 // inserted: function (el) { // console.log(el); // el.focus(); // }, // //updata鉤子函式,被繫結元素所在的模板更新時呼叫,不論值是否有變化,通過比較前後的值,可以忽略不必要的膜版更新 // updated() { // }, // //被繫結元素所在的模板完成一次更新週期時呼叫 // componentUpdated: function () { // }, // //只調用一次,指令與元素解綁呼叫 // unbind: function () {
// }








//三、動畫 //#1、動畫基礎 const animation1 = Vue.createApp({ "data": function () { return { "ok": true, "text": "", "content": "" } }, "methods": { //動畫生命週期 beforeEnter: function (el) { this.text = "進入之前" console.log(1); }, //enter的done引數,after引數失效 起作用為跳過css動畫 Enter: function (el) { // done(); this.content = "進入之中" console.log(2); }, afterEnter: function () { //根據持續時間duration去呼叫,此處為1000ms console.log(3); }, beforeLeave: function (el) { this.text = "離開之前" console.log(4); }, //enter的done引數,after引數失效 起作用為跳過css動畫 leave: function (el) { // done(); this.content = "離開之中" console.log(5); }, afterLeave: function () { //根據持續時間duration去呼叫,此處為1000ms console.log(6); },
},
}).mount('#animation1') //#2、動畫多元素過度 const animation2 = Vue.createApp({ data() { return { "show": 0,
} }, components: {
}, methods: {
}, }).mount('#animation2') //#3、多元件過度 const animation3 = Vue.createApp({ data() { return { "view": "aa",
} }, //不可同行盒子作為元件模板,可以用包括多個子盒子作為模板 components: { "aa": { "template": ` <div class='aa'> <div> 我是元件A1 </div> <div> 我是元件A2 </div> </div>`
}, "bb": { "template": ` <div class='bb'> 我是元件B </div>` } }, methods: {
},
}).mount("#animation3") //#4、多元素列表過度 const animation4 = Vue.createApp({ data() { return { "ok": true, "arr": [1, 2, 3, 4, 5, 6, 7, 8, 9], "num": 10 } },
methods: { add() {
this.arr.splice(parseInt(Math.random() * this.arr.length), 0, this.num) this.num++ console.log(parseInt(Math.random() * this.arr.length)); }, remove() { this.arr.splice(parseInt(Math.random() * this.arr.length), 1) }, change() { var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; var arr2 = []; for (let index = 0; index < 9; index++) { arr2.push(arr.splice(parseInt(Math.random() * arr.length), 1)[0])
} console.log(arr2); this.arr = arr2; } }, component: {
}, }).mount('#animation4')












//ref用法,作用:找到指定標籤的內容、標籤 const vue3 = Vue.createApp({ "data": function () { return {} }, "methods": { go() { this.$refs.div.innerText = '修改了div'; //<div>修改了div</div> console.log(this.$refs.div); //<div></div> } }, "components": { "lzx": { "data": function () { return { "msg": "massage", } }, "template": ` {{msg}} ` } } }).mount('#lzx'); //#provide+inject 父級多級傳值 爺爺元件給孫子元件傳值 //建立一個爺爺例項 const grandfather = Vue.createApp({ provide: { promsg: 'pro資訊,直接傳遞給孫子元件' }, data() { return { msg: '我是grandfather的資訊' } }, //用於區域性子元件的註冊,全域性元件不需要註冊 components: {
}, // 用於規定爺爺元件模板,其還可以放置全域性子元件或註冊過的區域性元件 template: ` <div> 1我是grandfather元件 </div> <p>--------------</p> <father :msg = 'msg' > </father> <p>--------------</p> ` }) grandfather.component('father', { props: ['msg'], template: ` <div > 2我是father元件 </div> <p>--------------</p> <p style='color:red'> 4father接收到: {{msg}} </p> <p>--------------</p> <child :msg='this.msg'></child> ` }) grandfather.component('child', { props: ['msg'], inject: ['promsg'], template: ` <div> 3我是child元件 </div> <p>--------------</p> <p style='color:pink'>5child收到 {{msg}}</p> <p style='color:purple'>5child收到 {{promsg}} </p> ` }) //將grandfather例項掛載到爺爺dom const gf = grandfather.mount('#grandfather') //一、全域性與區域性的元件註冊與使用 //2、區域性元件 //新的變數 在新變數裡面定義元件模板 而不是使用app.compontent,區域性元件只能在區域性,需要使用的話,需要在全域性根元件註冊 const counter = { data() { return { count: 0 } }, template: ` <div> <h1> 區域性元件 </h1> <button @click="count++"> 您點選了{{count}}次 </button> </div> ` } const liZongXiao = { data() { return { name: 'lzx' } }, template: `<div> <h1> 命名規則 </h1> </div> ` } //1、全域性元件 //#第一步定義基礎例項模板,建立vue的例項 const app = Vue.createApp({ data() { return { showComponent: 'sister1', msg: 'lzx', total: 0, name: "lzx12", pay: () => { alert('父元件訊息彈出') } } }, methods: { handleAppCounter(num) { this.total = num // console.log(num) }, swithImg() { //每呼叫一次改變一次值 this.showComponent = this.showComponent === 'sister1' ? 'sister2' : 'sister1'; } }, //#註冊區域性元件 components: { // 必須在全域性根元件中,註冊後才能使用 // counter 簡寫 可以直接註冊區域性元件 //或根據命名規範取名 counter, liZongXiao }, //使用全域性子元件:全部全域性子元件,拼成了大的根元件 template: ` <top-title></top-title> <describe></describe> <sum></sum> <sum></sum> <sum></sum> <counter></counter> <li-zong-xiao></li-zong-xiao> <son :name='name'></son> <daughter :pay='pay'></daughter> <girl :name='name'></girl> <boy :name='name'></boy> <total :total='total'></total> <hello style='color:red;' :msg='msg' ></hello> <chang-total :total1='total' @add='handleAppCounter'> </chang-total> <teacher > <div style='color:red'> {{msg}} </div> <div> <student> </student> </div> </teacher> <schoolmate > </schoolmate> <roommate> <template #one> <div> 具名插槽1 </div> </template> <template #two> <div> 具名插槽3 </div> </template> </roommate> <brother v-slot='props' > <span> {{props.item}}</span> </brother> <div style='margin-top:15px'> 動態元件component 配合keep-alive儲存虛擬DOM的資料</div> <keep-alive> <component :is='showComponent'> </component> </keep-alive> <div> <button @click='swithImg'> 切換圖片 </button> </div> <asyncComponent> </asyncComponent>
` }) //全域性子元件 不需要再父根元件components物件中註冊,則可以使用 //#父傳子 app.component('son', {
//props接收在全域性template中傳遞的值,下列使用在子元件的template props: ['name'], //靜態傳值只能傳遞字串,建議還是用動態傳值 template: `<div>{{typeof name}} </div>` }) //#子元件呼叫父元件的函式 app.component('daughter', { //props可以接收一個父元件中在template中繫結過來的的函式,使用this指標呼叫 props: ['pay'], methods: { handleClick() { alert('點選子元件呼叫父元件方法') this.pay()
} }, template: ` <div @click='this.handleClick'>點選子元件彈出父元件的資訊 </div> ` }) //#傳值型別的校驗 app.component('girl', { props: { //支援校驗型別:字串型別 布林型別 陣列型別 物件型別 函式型別 佔位符 name: String }, data() { return {
} }, template: `<div> {{name}} </div>` }) //#父元件必須在其template中傳值,不然丟擲警告 app.component('boy', { props: { name: { //此處規定傳入值的值必須為字串,也可以是其他型別 type: String, //validator後寫函式 validator: function (value) { //value=name值 //校驗value是否含有lzx字串,如果不含有,則為-1 return value.search('lzx') != -1 }, //必填校驗 //required: true, //使用者不填可以給予預設值 default: '預設值'
} }, template: ` <div> {{name}} </div> ` }) //#單項資料流:資料從父元件傳遞到子元件, app.component('total', { data() { return { //子元件不能直接改變父元件傳遞過來的值,如果需要修改,則需要通過另一個變數作為介質 newTotal: this.total } }, props: ['total'], template: ` {{newTotal}} <button @click='newTotal+=1'>增加數量</button> ` }) //#Non-props 子元件沒有接受父元件傳遞過來的引數 而子元件完全複製了父元件template中定義的模板 app.component('hello', { mounted() { //在業務邏輯中的使用 // console.log(this.$attrs.msg); }, //在元件元素上使用 //規定是否接受父元件中繫結過來的引數,一般為樣式或者類名,如果子元件中template存在父盒子包含子盒子,則子盒子負責接受其引數 //如果需要指定盒子進行接受引數,則可以通過v-bind='$attrs'繫結,但需要將父盒子去掉 例如第一個h4 //$attrs可以指定需要繫結的值,例如第二個h4 inheritAttrs: true, // props: ['msg'], template: `   <h4 v-bind='$attrs'> Hello word!1</h4>   <h4 v-bind:style='$attrs.style' >Hello word!2</h4>
<h4>Hello word!3</h4>   ` }) //#子傳父:子元件$emit呼叫父元件方法,傳值($emit傳遞引數),更改值(不需要介質,可以直接修改),校驗 app.component('changTotal', { props: ['total1'], //聲明瞭所有從父元件呼叫的函式,並進行校驗 emits: { add: (value) => { //校驗value==檢驗父元件傳遞來的this.total1 return value < 20 ? true : false } }, data() { return {
} }, methods: { Clicksonhandle() { //使用$emit方法呼叫父元件的方法 //同時可以向父元件傳遞引數 //直接修改父元件傳遞傳遞來的值 //操作邏輯都在子元件進行,父子解耦 this.$emit('add', this.total1 + 3) }
}, template: ` <div > 子元件接受父元件的值: {{total1}} <button @click='Clicksonhandle'> add</button> </div> ` }) //#元件插槽-Slot: app.component('teacher', { template: `<div> <span> <slot> </slot> </span>   </div>` }) //#在插槽中直接使用子元件 app.component('student', { template: `<span style="color:pink; font-size:50px "> student </span>` }) //#插槽作為備用內容:如果父元件給予內容樣式,則優先顯示父元件內容、樣式,否則使用插槽內的預設內容 app.component('schoolmate', { template: ` <div>   <slot> <span style='color:purple'>預設顯示內容 </span> </slot> </div> ` }) //#插槽的具名插槽的使用:給插槽命名,在父元件的template中使用template模板指定插槽位置 app.component('roommate', { template: ` <div> <slot name='one'></slot> <div> 具名插槽2 </div> <slot name='two'> </slot> </div> ` }) //#插槽的作用域:在子元件使用v-slot的資料,使用插槽的方式可以傳遞給父元件,在插槽繫結需要傳遞的值,在使用鍵值對的方式傳遞,然後在父元件tempalate中v-slot繫結鍵值對 app.component('brother', { data() { return { list: ['作用域1', '作用域2', '作用域3'] } }, template: ` <div> <ul> <li v-for="(item,index) in list"> {{item}} </li> </ul> <slot v-for='(item,index) in list' :item='item' > </slot> </div> ` }) //#動態元件 <component :is='showComponent'></component> app.component('sister1', { template: ` <img src='/tianmao/images/H1.jpg'> ` }) app.component('sister2', { // <img src='/tianmao/images/H4.jpg'> template: ` <input/> ` }) //#非同步元件 作用:1、在非同步請求遠端資料時,使用非同步元件緩載入資料 2、拆分大型網頁應用 app.component('asyncComponent', Vue.defineAsyncComponent( () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ template: `<div> 這是一個非同步元件 </div>` }) }, 3000); }) }
)) //#全域性子元件:使用便利,但會影響效能,區域性的元件,可以按需匯入 app.component('top-title', { template: '<h2>標題</h2>' }) //#全域性子元件 app.component('describe', { template: '<div>主體</div>' }) //#全域性子元件的複用 app.component('sum', { //data必須是一個函式,其閉包保證了每個複用元件的獨立性 data() { return { sum: 0 } }, template: ` <div> <button @click="sum++">您點選了{{sum}}次</button> </div> ` }) //#第二步將例項掛載到dom樹 const vm = app.mount("#app") </script>
</html>