Skip to content

Instantly share code, notes, and snippets.

@pjchender
Last active May 1, 2017 04:48
Show Gist options
  • Save pjchender/72f1953ed2d2338a5988b934d64077ea to your computer and use it in GitHub Desktop.
Save pjchender/72f1953ed2d2338a5988b934d64077ea to your computer and use it in GitHub Desktop.
[Flycan][Vue] CH3 - Part1(組件 component、轉場 transition、混合 mixin)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>03-01-registerComponent</title>
<link rel="stylesheet" type="text/css" href="css/normalize.css">
<link rel="stylesheet" type="text/css" href="css/layout.css">
<style type="text/css">
[v-cloak]{
display: none;
}
.global{
color: blue
}
.local{
color: red
}
.local .global{
color: red
}
</style>
</head>
<body>
<div id="app" v-cloak>
<!-- 使用全域組件 -->
<global-component></global-component>
<!-- 使用區域組件 -->
<local-component></local-component>
</div>
<script src="js/vue.js"></script>
<script>
// 建立全域組件
Vue.component('globalComponent', {
template:
`
<div class="global">
globalComponent
</div>
`
})
// 建立區域組件
var localComponent = {
template: `
<div class="local">
<h1>localComponent</h1>
<global-component></global-component>
</div>
`
}
var vm = new Vue({
el: '#app',
components: {
localComponent: localComponent
}
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>03-02-componentData</title>
<link rel="stylesheet" type="text/css" href="css/normalize.css">
<link rel="stylesheet" type="text/css" href="css/layout.css">
<style type="text/css">
[v-cloak]{
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak>
<!-- 使用 template -->
<counter></counter>
<counter></counter>
<counter></counter>
</div>
<script src="js/vue.js"></script>
<script>
// 代入資料到 component 中
var data = function () {
return {count: 0}
}
// 製作 component
Vue.component('counter', {
data: data,
template: `
<input type='button'
:value="count"
@click="count++"
>
`
})
var vm = new Vue({
el: '#app'
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>03-03-propsEmit</title>
<link rel="stylesheet" type="text/css" href="css/normalize.css">
<link rel="stylesheet" type="text/css" href="css/layout.css">
<style type="text/css">
[v-cloak]{
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak>
<!-- 父層組件 -->
<input type="button" :value="count" @click="count++">
<!-- 子層組件 -->
<!-- 將父層的資料帶給子層
1. :count-from-parent:在 HTML 中透過 v-bind 將資料從父層傳送到子層(在 HTML 中要使用 dash 命名)
2. props: ['countFromParent]:在 component 中透過 props 取得父層傳來的資料(在 JS 中使用駝峰式命名)
-->
<!-- 將子層的事件帶到父層
1. @click="$emit('increment'),透過 $emit('自定義事件名稱', '資料'),將事件從子組件打到父組件
2. @increment="count++",接收從 template 傳送過來的事件
-->
<counter @increment="count++" :count-from-parent="count"></counter>
<counter @increment="count++" :count-from-parent="count"></counter>
<counter @increment="count++" :count-from-parent="count"></counter>
</div>
<script src="js/vue.js"></script>
<script>
var data = {
count: 0
}
Vue.component('counter', {
props: ['countFromParent'], // 透過 props 接收從父組件傳送來的資料
template:
// 程式中用單引號
`
<input type="button"
:value="countFromParent"
@click="$emit('increment')"
>
`
})
var vm = new Vue({
el: '#app',
data: data
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>03-04-slideShow</title>
<link rel="stylesheet" type="text/css" href="css/normalize.css">
<link rel="stylesheet" type="text/css" href="css/layout.css">
<link rel="stylesheet" type="text/css" href="css/03-04-slideShow.css">
</head>
<body>
<div id="app" v-cloak>
<div class="container" v-if="imgList.length">
<!-- SlideComponent
1. :active="active":將資料從父層傳到 component 中
2. :img-list="imgList":將資料從父層傳到 component 中,記得要用 dash 命名
3. @next="nextHandler":接收從子層打上來的 next 事件
-->
<slide-component
:active="active"
:img-list="imgList"
@next="nextHandler"
></slide-component>
<!-- NavComponent
@change="changeHandler":接收從子層打上來的 change 事件
-->
<nav-component
:active="active"
:total="imgList"
@change="changeHandler"
>
</nav-component>
</div>
</div>
<script src="js/vue.js"></script>
<script src="js/superagent.js"></script>
<script>
(function (window) {
var data = {
active: 0,
imgList: [
'./images/children/0.jpg',
'./images/children/1.jpg',
'./images/children/2.jpg',
'./images/children/3.jpg',
'./images/children/4.jpg',
'./images/children/5.jpg',
'./images/children/6.jpg',
'./images/children/7.jpg'
]
}
var slideComponent = {
props: ['active', 'imgList'], // 使用父層傳來的資料
template:
// 程式中的字串用單引號
// @click="nextHandler",點一下圖片會換到下一張
`
<div class="img" :style="{left: -100 * active + '%'}">
<img
v-for="(item, index) in imgList"
:src="item"
:style="{left: 100 * index + '%'}"
@click="nextHandler"
>
</div>
`,
methods: {
nextHandler () {
this.$emit('next') // 打 next 事件到父層
}
}
}
var navComponent = {
props: ['active', 'total'],
template:
// 點擊 nav 的時候要打事件到 root
`
<div class="nav">
<a href="javascript:;"
v-for="(item, index) in total"
:class="{active: active === index}"
@click="clickHandler(index)"
>
</a>
</div>
`,
methods: {
clickHandler (index) {
this.$emit('change', index)
}
}
}
var vm = new Vue({
el: '#app',
data: data,
components: {
slideComponent: slideComponent,
navComponent: navComponent
},
methods: {
changeHandler (index) {
this.active = index
},
nextHandler () {
this.active = (this.active + 1) % this.imgList.length
}
}
})
})(window)
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>03-05-alphaShow</title>
<link rel="stylesheet" type="text/css" href="css/normalize.css">
<link rel="stylesheet" type="text/css" href="css/layout.css">
<style type="text/css">
[v-cloak]{
display: none;
}
.container{
position: relative;
width: 550px;
padding-top: 300px;
overflow: hidden;
}
.img{
position: absolute;
top:0%;
left:0%;
width: 100%;
height: 300px;
transition: left 0.5s;
}
.img > img{
position: absolute;
top: 0%;
left: 0%;
}
.nav{
text-align: center
}
.nav > a{
display: inline-block;
width: 20px;
height: 20px;
border-radius: 10px;
margin: 5px;
background-color: white;
}
.nav > a.active{
background-color: black;
}
/**
* 根據 transition 的 name 建立 Vue 中 transition 的 class
**/
.fade-enter-active, .fade-leave-active {
transition: opacity .4s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
</style>
</head>
<body>
<div id="app" v-cloak>
<div class="container" v-if="imgList.length">
<alpha-component
:active="active"
:list="imgList"
@next="nextHandler">
</alpha-component>
<nav-component
:active="active"
:total="imgList.length"
@change="changeHandler">
</nav-component>
</div>
</div>
<script src="js/vue.js"></script>
<script src="js/superagent.js"></script>
<script>
(function (window) {
var data = {
active: 0,
imgList: [
'./images/children/0.jpg',
'./images/children/1.jpg',
'./images/children/2.jpg',
'./images/children/3.jpg',
'./images/children/4.jpg',
'./images/children/5.jpg',
'./images/children/6.jpg',
'./images/children/7.jpg'
]
}
var alphaComponent = {
props: ['active', 'list'],
template:
/**
* 一開始沒有淡入淡出的效果
* 透過 <transition> 告訴 Vue 這裡要套用過渡狀態
* name="fade",用來自定義 css 別名
**/
/**
* 把 v-if 改成 v-show,因為 v-if 在這裡會動態的改變 DOM 導致每次圖檔都需要重新 request 和重新載入
* 而 v-show 會一次把圖檔全部載入,不用重複 request
**/
/**
* 用 transition-group 的話,裡面的內容要加上 :key = "index"
* tag 告訴它外面要包的 tag 是 div (預設是 span)
**/
`
<transition-group name="fade" appear class="img" @click="nextHandler" tag="div">
<img :key="index" :src="item" v-show="active == index" v-for="(item,index) in list">
</transition-group>
`,
methods: {
nextHandler: function () {
this.$emit('next')
}
}
}
var navComponent = {
props: ['active', 'total'],
template: `
<div class="nav">
<a href="javascript:;" v-for="index in total"
:class="{active : active == index-1}"
@click="clickHandler(index-1)"></a>
</div>
`,
methods: {
clickHandler: function (index) {
this.$emit('change', index)
}
}
}
var vm = new Vue({
el: '#app',
data: data,
components: {
alphaComponent: alphaComponent,
navComponent: navComponent
},
methods: {
changeHandler: function (index) {
this.active = index
},
nextHandler: function () {
this.active = (this.active + 1) % this.imgList.length
}
}
})
})(window)
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>03-06-mixin</title>
<link rel="stylesheet" type="text/css" href="css/normalize.css">
<link rel="stylesheet" type="text/css" href="css/layout.css">
<style type="text/css">
[v-cloak]{
display: none;
}
.container{
position: relative;
width: 550px;
padding-top: 300px;
overflow: hidden;
}
.img{
position: absolute;
top:0%;
left:0%;
width: 100%;
height: 300px;
transition: left 0.5s;
}
.img > img{
position: absolute;
top: 0%;
left: 0%;
}
.nav{
text-align: center
}
.nav > a{
display: inline-block;
width: 20px;
height: 20px;
border-radius: 10px;
margin: 5px;
background-color: white;
}
.nav > a.active{
background-color: black;
}
.fade-enter-active {
animation: fade-in .5s;
}
.fade-leave-active {
animation: fade-out .5s;
}
@keyframes fade-in {
0% {
opacity: 0;
}
50% {
opacity: 0.75;
}
100% {
opacity: 1;
}
}
@keyframes fade-out {
0% {
opacity: 1;
}
50% {
opacity: 0.75;
}
100% {
opacity: 0;
}
}
</style>
</head>
<body>
<div id="app" v-cloak>
<div class="container" v-if="imgList.length">
<slide-component
:active="slide"
:list="imgList"
@next="nextHandler('slide')">
</slide-component>
<nav-component
:active="slide"
:total="imgList.length"
@change="changeHandler($event,'slide')">
</nav-component>
</div>
<div class="container" v-if="imgList.length">
<alpha-component
:active="alpha"
:list="imgList"
@next="nextHandler('alpha')">
</alpha-component>
<nav-component
:active="alpha"
:total="imgList.length"
@change="changeHandler($event,'alpha')">
</nav-component>
</div>
</div>
<script src="js/vue.js"></script>
<script src="js/superagent.js"></script>
<script>
(function (window) {
var data = {
slide: 0,
alpha: 0,
imgList: [
'./images/children/0.jpg',
'./images/children/1.jpg',
'./images/children/2.jpg',
'./images/children/3.jpg',
'./images/children/4.jpg',
'./images/children/5.jpg',
'./images/children/6.jpg',
'./images/children/7.jpg'
]
}
// 將重複的內容定義成 mixin
// 定義一個 mixin
var imgMixin = {
props: ['active', 'list'],
methods: {
nextHandler: function () {
this.$emit('next')
}
}
}
// 定義使用 mixin 物件的組件
var slideComponent = {
mixins: [imgMixin],
template: `
<div class="img" :style="{left:-100*active+'%'}"
@click="nextHandler">
<img :src="item" v-for="(item,index) in list"
:style="{left:100*index+'%'}">
</div>
`
}
var alphaComponent = {
mixins: [imgMixin],
template: `
<div class="img" @click="nextHandler" style="overflow:hidden;">
<transition name="fade" v-for="(item,index) in list" appear>
<img :src="item" v-if="active == index">
</transition>
</div>
`
}
var navComponent = {
props: ['active', 'total'],
template: `
<div class="nav">
<a href="javascript:;" v-for="index in total"
:class="{active : active == index-1}"
@click="clickHandler(index-1)"></a>
</div>
`,
methods: {
clickHandler: function (index) {
this.$emit('change', index)
}
}
}
var vm = new Vue({
el: '#app',
data: data,
components: {
slideComponent: slideComponent,
alphaComponent: alphaComponent,
navComponent: navComponent
},
methods: {
changeHandler: function (index, type) {
this[type] = index
},
nextHandler: function (type) {
this[type] = (this[type] + 1) % this.imgList.length
}
}
})
})(window)
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>03-07-animateCss</title>
<link rel="stylesheet" type="text/css" href="css/normalize.css">
<link rel="stylesheet" type="text/css" href="css/layout.css">
<link rel="stylesheet" type="text/css" href="css/animate.min.css">
<style type="text/css">
[v-cloak]{
display: none;
}
body{
text-align: center;
}
a{
display: inline-block;
text-decoration: none;
color: #0769AD;
background-color: #ffffff;
padding: 5px 10px;
font-size: 12px;
line-height: 20px;
}
</style>
</head>
<body>
<div id="app" v-cloak>
<!--
- 直接使用 css 的方式套用轉場效果
- 這裡套用的是 animatedCSS
-->
<transition
enter-active-class="animated tada"
leave-active-class="animated tada"
>
<img :src="src" v-if="show">
</transition>
<br>
<a href="javascript:;" @click="switchHandler">Switch</a>
</div>
<script src="js/vue.js"></script>
<script src="js/superagent.js"></script>
<script>
(function (window) {
var data = {
src: "./images/children/0.jpg",
show: true
}
var vm = new Vue({
el: "#app",
data: data,
methods:{
switchHandler: function () {
this.show = !this.show
}
}
})
})(window)
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>03-05-alphaShow</title>
<link rel="stylesheet" type="text/css" href="css/normalize.css">
<link rel="stylesheet" type="text/css" href="css/site.css">
<style type="text/css">
.active{
font-weight: bolder;
color: #ffffff;
background-color: #40c297;
}
</style>
</head>
<body>
<div id="app" v-cloak>
<header>
<h1>Flycan 飛肯設計學苑</h1>
<div>
<!-- 點擊的時候改變 view 的名稱 -->
<a href="javascript:;"
v-for="item in menu"
:class="{active:view == item.name}"
@click="view=item.name">
{{item.text}}
</a>
</div>
</header>
<!-- :css="false" 解除 Vue 對 CSS 的偵測-->
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@leave="leave"
@after-leave="afterLeave"
:css="false"
>
<!-- 利用 v-bind:is="xxx" 選擇要顯示的 component -->
<component v-bind:is="view"></component>
</transition>
</div>
<script src="js/vue.js"></script>
<script src="js/velocity.min.js"></script>
<script>
(function(window){
new Vue({
el: '#app',
data: {
view: 'index',
menu:[
{text:'首頁',name:'index'},
{text:'課程',name:'course'},
{text:'聯絡',name:'contact'},
]
},
components: {
'index': {
template: `
<div class="page">
<h2>首頁組件</h2>
</div>
`
},
'course': {
template: `
<div class="page">
<h2>課程組件</h2>
</div>
`
},
'contact': {
template: `
<div class="page">
<h2>聯絡組件</h2>
</div>
`
},
},
methods: {
// 準備需要的資料
beforeEnter: function(el){
el.style.opacity = 0
el.style.position = 'absolute' // 避免兩個組件衝突,讓螢幕上一次指出現一個組件
},
// 動畫效果
enter: function(el, done){
Velocity(el, {opacity: 1}, {
duration: 500,
complete: done
})
},
// 對新的 DOM 綁定一些事件
afterEnter: function (el, done) {
el.style.cssText = ''
},
// 解除對 DOM 綁定的事件
beforeLeave: function(){
console.log('beforeLeave')
},
// 動畫
leave: function (el, done) {
Velocity(el, { opacity: 0 }, {
duration: 500 ,
complete: done
})
},
// 清除不必要的資料
afterLeave: function (el, done) {
console.log('component out')
}
}
})
})(window)
</script>
</body>
</html>
@pjchender
Copy link
Author

03-08 使用 JavaScript 鉤子製作轉場效果

  • el 會自動回傳進退場的對象
  • 從 enter 進到 after enter 需要傳 done
  • 當只用 JavaScript 過渡的時候, 在 enter 和 leave 中,回調函數 done 是必須的 。
  • 推薦對於僅使用 JavaScript 過渡的元素添加 v-bind:css="false",Vue 會跳過 CSS 的檢測。這也可以避免過渡過程中 CSS 的影響。
<transition
  v-on:before-enter="beforeEnter"
  v-on:enter="enter"
  v-on:after-enter="afterEnter"
  v-on:enter-cancelled="enterCancelled"
  v-on:before-leave="beforeLeave"
  v-on:leave="leave"
  v-on:after-leave="afterLeave"
  v-on:leave-cancelled="leaveCancelled"
>
  <!-- ... -->
</transition>
beforeEnter: function (el) {
  //  el 指進退場的對象
  //  通常是用來綁定資料
  el.style.opcaity = 0
  el.style.position = 'absolute'
},
enter: function (el, done) {
  //  通常用來執行動畫
  //  Velocity 是用來製作動畫的第三方套件
  Velocity(el, { opacity: 1 }, { duration: 500, complete: done })
},
afterEnter: function (el) {
  //  通常這裡是在綁定一些事件
  el.style.cssText = '' //  可以不用手動清除,這裡只是當作範例
},
beforeLeave: function (el) {
  // 通常在這裡解除綁定事件
},
leave: function (el, done) {
  // ...
  done()
},
afterLeave: function (el) {
  // 通常在這裡清除不必要的資料
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment