PJCHENder
4/3/2017 - 3:06 PM

[Flycan][Vue] CH3 - Part1(組件 component、轉場 transition、混合 mixin)

[Flycan][Vue] CH3 - Part1(組件 component、轉場 transition、混合 mixin)

<!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>
<!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-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-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-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-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-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-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>