Weex內(nèi)置組件的使用——Weex的學(xué)習(xí)之路(五)

這幾天忙著做組件化了,博客晚了些更新,說到組件化,我近期也會用博客來介紹的。上一篇博客我們講的是weex的內(nèi)置組件,那么這篇博客我們學(xué)習(xí)最后四個組件,下篇博客我們就會講到世界weex頁面的編寫了。這篇博客我們來學(xué)習(xí)<waterfall>、<video>、<web>和<richtext>。weex的組件真的是要好好學(xué)牢固的,很多人可能會覺得定義太多記不住,沒關(guān)系,在后續(xù)實(shí)際頁面的編寫上我們還是會溫習(xí)的。

1.<waterfall>的簡介和使用

<waterfall> 組件是提供瀑布流布局的核心組件。瀑布流,又稱瀑布流式布局是比較流行的一種頁面布局,視覺表現(xiàn)為參差不齊的多欄布局。隨著頁面滾動條向下滾動,這種布局還可以不斷加載數(shù)據(jù)塊并附加至當(dāng)前尾部。

<template>
  <waterfall column-count="2" column-width="auto">
    <cell v-for="num in lists" >
      <text>{{num}}</text>
    </cell>
  </waterfall>
</template>
<script>
  export default {
    data () {
      return {
        lists: ['A', 'B', 'C', 'D', 'E']
      }
    }
  }
</script>

<style></style>

<waterfall>的子組件

<list>組件一樣, <waterfall> 組件的子組件只能包括以下四種組件或是 fix 定位的組件,其他形式的組件將不能被正確渲染。

  • <cell>:用于定義列表中的子列表項(xiàng),類似于 HTML 中的 ul 之于 li。Weex 會對 <cell> 進(jìn)行高效的內(nèi)存回收以達(dá)到更好的性能。
  • <header>:當(dāng) <header> 到達(dá)屏幕頂部時,吸附在屏幕頂部。
  • <refresh>:用于給列表添加下拉刷新的功能。
  • <loading><loading> 用法與特性和 <refresh> 類似,用于給列表添加上拉加載更多的功能。

<waterfall>的屬性

  • show-scrollbar : [可選] 可選值為 true/ false,默認(rèn)值為 true。控制是否出現(xiàn)滾動條。 [H5無效]
  • column-count: [可選]描述瀑布流的列數(shù)
    • auto: 意味著列數(shù)是被其他屬性所決定的(比如 column-width)
    • <integer>: 最佳列數(shù),column-width 和 column-count 都指定非0值, 則 column-count 代表最大列數(shù)。
  • column-width : [可選]描述瀑布流每一列的列寬
    • auto: 意味著列寬是被其他屬性所決定的(比如 column-count)
    • <length>: 最佳列寬,實(shí)際的列寬可能會更寬(需要填充剩余的空間), 或者更窄(如果剩余空間比列寬還要小)。 該值必須大于0
  • column-gap: [可選]列與列的間隙. 如果指定了 normal
  • left-gap: [可選]左邊cell和列表的間隙. 如果未指定 。
  • right-gap: [可選]右邊cell和列表的間隙. 如果未指定。

<waterfall>的事件

支持所有通用事件:

  • click:用于監(jiān)聽點(diǎn)擊事件。(例如:一般綁定于子組件之上觸發(fā)跳轉(zhuǎn))。
  • longpress:用于監(jiān)聽長按事件(一般綁定于子組件之上例如:手機(jī)淘寶猜你喜歡瀑布流,長按可刪除您不感興趣的商品)。
  • appear:用于監(jiān)聽子組件出現(xiàn)事件(一般綁定于子組件之上例如:監(jiān)聽最后一個元素出現(xiàn),加載新的數(shù)據(jù))
  • disappear:用于監(jiān)聽子組件滑出屏幕事件(一般綁定于子組件之上)

<waterfall>使用示例如下:

<template>
  <waterfall class="page" ref="waterfall"
  v-bind:style="{padding:padding}"
  :column-width="columnWidth" :column-count="columnCount" :column-gap="columnGap"
  :show-scrollbar="showScrollbar" :scrollable="scrollable"
  @scroll="recylerScroll" @loadmore="loadmore" loadmoreoffset=3000
  >
    <refresh class="refresh" @refresh="onrefresh" @pullingdown="onpullingdown" :display="refreshing ? 'show' : 'hide'">
      <loading-indicator class="indicator"></loading-indicator>
      <text class="refreshText">{{refreshText}}</text>
    </refresh>
    <header class="stickyHeader" >
      <div v-if="stickyHeaderType === 'none'" class="stickyWrapper">
        <text class="stickyText">Header</text>
      </div>
      <div v-if="stickyHeaderType === 'appear'" class="stickyWrapper">
        <div class="stickyTextImageWrapper">
          <text class="stickyText">Last Appear:</text>
          <image class="stickyImage" :src="appearImage"></image>
        </div>
        <div class="stickyTextImageWrapper">
          <text class="stickyText">Last Disappear:</text>
          <image class="stickyImage" :src="disappearImage"></image>
        </div>
      </div>
      <div v-if="stickyHeaderType === 'scroll'" class="stickyWrapper">
        <text class="stickyText">Content Offset:{{contentOffset}}</text>
      </div>
    </header>
    <cell v-for="(item, index) in items" :key="item.src" class="cell" ref="index">
      <div class="item" @click="onItemclick(item.behaviour, index)" @appear="itemAppear(item.src)" @disappear="itemDisappear(item.src)">
        <text v-if="item.name" class="itemName">{{item.name}}</text>
        <image class="itemPhoto" :src="item.src"></image>
        <text v-if="item.desc" class="itemDesc">{{item.desc}}</text>
        <text v-if="item.behaviourName" class="itemClickBehaviour"> {{item.behaviourName}}</text>
      </div>
    </cell>
    <header class="footer" ref="footer">
      <text class="stickyText">Footer</text>
    </header>
    <div ref="fixed" class="fixedItem" @click="scrollToNext">
      <text class="fixedText">bot</text>
    </div>
  </waterfall>
</template>

<style>
  .page {
    background-color: #EFEFEF;
  }
  .refresh {
    height: 128px;
    width: 750px;
    flex-direction: row;
    align-items: center;
    justify-content: center;
  }
  .refreshText {
    color: #888888;
    font-weight: bold;
  }
  .indicator {
    color: #888888;
    height: 40px;
    width: 40px;
    margin-right: 30px;
  }
  .absolute {
  position: absolute;
  top: 0px;
  width: 750px;
  height: 377px;
}
  .avatar {
    width: 148px;
    height: 108px;
    border-radius: 54px;
    border-width: 4px;
    border-color: #FFFFFF;
    margin-bottom: 14px;
  }
  .name {
    font-weight: bold;
    font-size:32px;
    color:#ffffff;
    line-height:32px;
    text-align:center;
    margin-bottom: 16px;
  }
  .titleWrap {
    width: 100px;
    height: 24px;
    margin-bottom: 10px;
    background-color: rgba(255,255,255,0.80);
    border-radius: 12px;
    justify-content: center;
    align-items: center;
  }
  .title {
    font-size: 20px;
    color: #000000;
  }
  .stickyHeader {
    position: sticky;
    height: 94px;
    flex-direction: row;
    padding-bottom:6px;
  }
  .stickyWrapper {
    flex-direction: row;
    background-color:#00cc99;
    justify-content: center;
    align-items: center;
    flex:1;
  }
  .stickyTextImageWrapper {
    flex:1;
    justify-content: center;
    align-items: center;
    flex-direction: row;
  }
  .stickyText {
    color: #FFFFFF;
    font-weight: bold;
    font-size:32px;
    margin-right: 12px;
  }
  .stickyImage {
    width: 64px;
    height: 64px;
    border-radius: 32px;
  }

  .cell {
    padding-top: 10px;
    padding-bottom: 10px;
  }
  .item {
    padding: 10px;
    background-color: #FFFFFF;
    align-items: center;
  }
  .itemName {
    font-size:28px;
    color:#333333;
    line-height:42px;
    text-align:left;
    margin-top: 24px;
  }
  .itemPhoto {
    margin-top: 18;
    width: 220px;
    height: 220px;
    margin-bottom: 18px;
  }
  .itemDesc {
    font-size:24px;
    margin:12px;
    color:#999999;
    line-height:36px;
    text-align:left;
  }
  .itemClickBehaviour {
    font-size:36px;
    color:#00cc99;
    line-height:36px;
    text-align:center;
    margin-top: 6px;
    margin-left: 24px;
    margin-right: 24px;
    margin-bottom: 30px;
  }
  .footer {
    height: 94px;
    justify-content: center;
    align-items: center;
    background-color: #00cc99;
  }

  .fixedItem {
    position: fixed;
    width:78px;
    height:78px;
    background-color:#00cc99;
    right: 32px;
    bottom: 32px;
    border-radius: 39px;
    align-items: center;
    justify-content: center;
  }
  .fixedText {
    font-size: 32px;
    color: white;
    line-height: 32px;
  }

</style>

<script>
  export default {
    data: function() {
      const items = [
        {
          src:'https://gw.alicdn.com/tps/TB1Jl1CPFXXXXcJXXXXXXXXXXXX-370-370.jpg',
          name: 'Thomas Carlyle',
          desc:'Genius only means hard-working all one\'s life',
          behaviourName: 'Change width',
          behaviour: 'changeColumnWidth',
        },
        {
          src:'https://gw.alicdn.com/tps/TB1Hv1JPFXXXXa3XXXXXXXXXXXX-370-370.jpg',
          desc:'The man who has made up his mind to win will never say "impossible "',
          behaviourName: 'Change gap',
          behaviour: 'changeColumnGap'
        },
        {
          src:'https://gw.alicdn.com/tps/TB1eNKuPFXXXXc_XpXXXXXXXXXX-370-370.jpg',
          desc:'There is no such thing as a great talent without great will - power',
          behaviourName: 'Change count',
          behaviour: 'changeColumnCount'
        },
        {
          src:'https://gw.alicdn.com/tps/TB1DCh8PFXXXXX7aXXXXXXXXXXX-370-370.jpg',
          name:'Addison',
          desc:'Cease to struggle and you cease to live',
          behaviourName: 'Show scrollbar',
          behaviour: 'showScrollbar',
        },
        {
          src:'https://gw.alicdn.com/tps/TB1ACygPFXXXXXwXVXXXXXXXXXX-370-370.jpg',
          desc:'A strong man will struggle with the storms of fate',
          behaviourName: 'Listen appear',
          behaviour: 'listenAppear',
        },
        {
          src:'https://gw.alicdn.com/tps/TB1IGShPFXXXXaqXVXXXXXXXXXX-370-370.jpg',
          name:'Ruskin',
          desc:'Living without an aim is like sailing without a compass',
          behaviourName: 'Set scrollable',
          behaviour: 'setScrollable',
        },
        {
          src:'https://gw.alicdn.com/tps/TB1xU93PFXXXXXHaXXXXXXXXXXX-240-240.jpg',
          behaviourName: 'waterfall padding',
          behaviour: 'setPadding',
        },
        {
          src:'https://gw.alicdn.com/tps/TB19hu0PFXXXXaXaXXXXXXXXXXX-240-240.jpg',
          name:'Balzac',
          desc:'There is no such thing as a great talent without great will - power',
          behaviourName: 'listen scroll',
          behaviour: 'listenScroll',
        },
        {
          src:'https://gw.alicdn.com/tps/TB1ux2vPFXXXXbkXXXXXXXXXXXX-240-240.jpg',
          behaviourName: 'Remove cell',
          behaviour: 'removeCell',
        },
        {
          src:'https://gw.alicdn.com/tps/TB1tCCWPFXXXXa7aXXXXXXXXXXX-240-240.jpg',
          behaviourName: 'Move cell',
          behaviour: 'moveCell',
        }
      ]

      let repeatItems = [];
      for (let i = 0; i < 3; i++) {
        repeatItems.push(...items)
      }

      return {
        padding: 0,
        refreshing: false,
        refreshText: '↓   pull to refresh...',
        columnCount: 2,
        columnGap: 12,
        columnWidth: 'auto',
        contentOffset: '0',
        showHeader: true,
        showScrollbar: true,
        scrollable: true,
        showStickyHeader: false,
        appearImage: null,
        disappearImage: null,
        stickyHeaderType: 'none',
        // fixedRect:'',
        items: repeatItems
      }
    },

    created() {
      // let self = this
      // setTimeout(()=>{
      //   weex.requireModule('dom').getComponentRect(this.$refs.fixed, result=>{
      //     const x = result.size.left
      //     const y = result.size.top
      //     const width = result.size.width
      //     const height = result.size.height
      //     self.fixedRect = `${x}|${y}|${width}|${height}`
      //   })
      // }, 3000)
    },

    methods: {
      recylerScroll: function(e) {
        this.contentOffset = e.contentOffset.y
      },
      loadmore: function(e) {
        console.log('receive loadmore event')
        // this.$refs.waterfall.resetLoadmore()
      },
      showOrRemoveHeader: function() {
        this.showHeader = !this.showHeader
      },
      onItemclick: function (behaviour, index) {
        console.log(`click...${behaviour} at index ${index}`)
        switch (behaviour) {
          case 'changeColumnCount':
            this.changeColumnCount()
            break
          case 'changeColumnGap':
            this.changeColumnGap()
            break
          case 'changeColumnWidth':
            this.changeColumnWidth()
            break
          case 'showScrollbar':
            this.showOrHideScrollbar()
            break
          case 'listenAppear':
            this.listenAppearAndDisappear()
            break
          case 'setScrollable':
            this.setScrollable()
            break
          case 'setPadding':
            this.setRecyclerPadding()
            break
          case 'listenScroll':
            this.listenScrollEvent()
            break
          case 'removeCell':
            this.removeCell(index)
            break
          case 'moveCell':
            this.moveCell(index)
            break
        }
      },

      itemAppear: function(src) {
        this.appearImage = src;
      },

      itemDisappear: function(src) {
        this.disappearImage = src;
      },

      changeColumnCount: function() {
        if (this.columnCount === 2) {
          this.columnCount = 3
        } else {
          this.columnCount = 2
        }
      },

      changeColumnGap: function() {
        if (this.columnGap === 12) {
          this.columnGap = 'normal'
        } else {
          this.columnGap = 12
        }
      },

      changeColumnWidth: function() {
        if (this.columnWidth === 'auto') {
          this.columnWidth = 600
        } else {
          this.columnWidth = 'auto'
        }
      },

      showOrHideScrollbar: function() {
        this.showScrollbar = !this.showScrollbar
      },

      setScrollable: function() {
        this.scrollable = !this.scrollable
      },

      listenAppearAndDisappear: function() {
        this.stickyHeaderType = (this.stickyHeaderType === 'appear' ? 'none' : 'appear')
      },

      listenScrollEvent: function() {
        this.stickyHeaderType = (this.stickyHeaderType === 'scroll' ? 'none' : 'scroll')
      },

      scrollToNext: function() {
        weex.requireModule('dom').scrollToElement(this.$refs.footer)
      },

      setRecyclerPadding: function() {
        this.padding = (this.padding == 0 ? 12 : 0);
      },

      removeCell: function(index) {
        this.items.splice(index, 1)
      },

      moveCell: function(index) {
        if (index == 0) {
          this.items.splice(this.items.length - 1, 0, this.items.splice(index, 1)[0]);
        } else {
          this.items.splice(0, 0, this.items.splice(index, 1)[0]);
        }
      },

      onrefresh (event) {
        this.refreshing = true
        this.refreshText = "loading..."
        setTimeout(() => {
          this.refreshing = false
          this.refreshText = '↓   pull to refresh...'
        }, 2000)
      },

      onpullingdown (event) {
        if (event.pullingDistance < -64) {
          this.refreshText = '↑   release to refresh...'
        } else {
          this.refreshText = '↓   pull to refresh...'
        }
      }
    }
  }
</script>

2.<video>的簡介和使用

<video>的簡介

Video 組件用于在頁面中嵌入視頻內(nèi)容。

<video>的子組件

text 是唯一合法的子組件。

<video>的屬性

  • src, string. 內(nèi)嵌的視頻指向的URL。
  • play-status, string. 可選值為 play | pause,用來控制視頻的播放狀態(tài),play 或者 pause,默認(rèn)值是 pause。
  • auto-play, boolean. 當(dāng)頁面加載初始化完成后,用來控制視頻是否立即播放,默認(rèn)值是 false。
  • poster, string, v0.18+ & iOS. 指定視頻首圖的圖片鏈接。
  • controls, string, v0.19+. 可選值為 controls | nocontrols,控制視頻播放組件是否顯示回放控制面板,默認(rèn)會顯示,當(dāng)指定為 nocontrols 時不顯示回放控制面板。

<video>的事件

  • start 當(dāng) playback 的狀態(tài)是 Playing 時觸發(fā)。
  • pause 當(dāng) playback 的狀態(tài)是 Paused 時觸發(fā)。
  • finish 當(dāng) playback 的狀態(tài)是 Finished 時觸發(fā)。
  • fail 當(dāng) playback 狀態(tài)是 Failed 時觸發(fā)。

<video>的示例:

<template>
  <div>
    <video class="video" :src="src" autoplay controls
      @start="onstart" @pause="onpause" @finish="onfinish" @fail="onfail"></video>
    <text class="info">state: {{state}}</text>
  </div>
</template>

<style scoped>
  .video {
    width: 630px;
    height: 350px;
    margin-top: 60px;
    margin-left: 60px;
  }
  .info {
    margin-top: 40px;
    font-size: 40px;
    text-align: center;
  }
</style>

<script>
  export default {
    data () {
      return {
        state: '----',
        src:'http://flv2.bn.netease.com/videolib3/1611/01/XGqSL5981/SD/XGqSL5981-mobile.mp4'
      }
    },
    methods:{
      onstart (event) {
        this.state = 'onstart'
      },
      onpause (event) {
        this.state = 'onpause'
      },
      onfinish (event) {
        this.state = 'onfinish'
      },
      onfail (event) {
        this.state = 'onfinish'
      }
    }
  }
</script>

3.<web>的簡介和使用

<web>的簡介

<web> 用于在 WEEX 頁面中顯示由 src 屬性指定的網(wǎng)頁內(nèi)容。

<web> 可以使 H5 與 Native 元素相結(jié)合。

  • 您可以整個頁面鋪滿 Web 頁面(快速兼容您之前的 H5 頁面)
  • 可以使用 Web 和其他 Weex 組件合成復(fù)雜頁面
  • 使用 Web 組合出多種效果(設(shè)置透明背景的 H5 頁面,靈活配置各類 H5 活動資訊)
<template>
  <web src="https://www.taobao.com/"></web>
</template>
<script></script>

<style></style>

特別注意事項(xiàng)

  • <web> 不支持任何嵌套的子組件。
  • <web> 必須指定 width 和 height 的樣式屬性,否則將不起作用。
  • 您可以使用 webview module來控制 <web>。

<web>的屬性

src是要加載的網(wǎng)頁內(nèi)容的 URL。建議指定線上真實(shí)存在的 URL 地址。

<web>的事件

支持 appear 和 disappear 事件同時支持:

  • pagestart 會在 Web 頁面開始加載時調(diào)用。

    事件對象:

    • url: {String} 當(dāng)前 Web 頁面的 URL。
  • pagefinish 會在 Web 頁面完成加載時調(diào)用。

    事件對象:

    • url: {String} 當(dāng)前 Web 頁面的 URL。
    • canGoBack: {Boolean} 當(dāng)前 Web 頁面是否可以回退。
    • canGoForward: {Boolean} 當(dāng)前 Web 頁面是否可以前進(jìn)。
    • title: {String} 當(dāng)前 Web 頁面的標(biāo)題(僅限 iOS 平臺)。
  • error 會在 Web 頁面加載失敗時調(diào)用。

  • receivedtitle 會在 Web 頁面的標(biāo)題發(fā)生改變時調(diào)用(僅限 Android 平臺)。

<template>
  <div class="wrapper">
    <web @pagestart="onPageStart" @pagefinish="onPageFinish" @error="onError" src="https://www.taobao.com/"></web>
  </div>
</template>
<script>
  module.exports = {
    methods: {
      onPageStart: function(e) {
         // page start load
      },
      onPageFinish: function(e) {
         // page finish load
      },
      onError: function(e) {
         // page load error
      }
    }
  }
</script>

<style></style>

4.<richtext>的簡介和使用

<richtext>的簡介

富文本組件可以內(nèi)嵌 <span>``<a>``<image>。同時它也支持 <span>``<a>``<image> 的嵌套。

只有 <span>, <a> and <image> 可以包含在 <richtext> 標(biāo)簽里。<span> and <a> 會被顯示為 display:inline,而 <image> 會被顯示為 display:inline-block

<richtext> 的子節(jié)點(diǎn)分兩種類型。

  • <span> and <a> 可以再包含孩子節(jié)點(diǎn)。
  • <image> 不能再包含孩子節(jié)點(diǎn)。

富文本組件內(nèi)部樹形結(jié)構(gòu)不能超過255層,超過的層會被忽略。

注意事項(xiàng)

  • <a> 標(biāo)簽在 iOS 上恒定為 color:blue 藍(lán)色樣式,它孩子節(jié)點(diǎn)也會被應(yīng)用為該樣式,見下面樣例。Android 上無此限制。
  • <image> 標(biāo)簽必須指定 widthheight.
  • 在圖片下載完成前,<image> 會保持空白狀態(tài),目前不支持顯示占位圖。
  • 富文本組件自身不能嵌套。
  • <span>, <a><richtext>
    • 可以被繼承
      • color
      • font-family
      • font-size
      • font-style
      • font-weight
      • line-height
    • 不可被繼承
      • background-color
  • <span>
    • 可以被繼承
      • text-decoration: none | underline | line-through, 默認(rèn)值是 none
  • <richtext>
    • 不可被繼承
      • lines: 最大行數(shù),必須為正數(shù)。
  • <image>
    • 不可被繼承
      • width
      • height
  • 通用事件 支持所有通用事件
  • itemclick. 只有imgspan標(biāo)簽可能觸發(fā),觸發(fā)時機(jī)是:
    • img標(biāo)簽:
      • img被點(diǎn)擊時沒有任何父節(jié)點(diǎn)是 a
      • 如果第一個條件不滿足,Weex 會嘗試打開 a 標(biāo)簽指定的鏈接。
      • imgpseudo-ref 會作為參數(shù)傳回來。

span標(biāo)簽:

  • a標(biāo)簽中的span被點(diǎn)擊
  • 并且所在的a標(biāo)簽的href被指定為"click://"(這個條件iOS端強(qiáng)要求,Android端并不要求)
  • a標(biāo)簽設(shè)置了pseudo-ref。此時itemclick事件會被觸發(fā),并且攜帶pseudo-ref的值。

<richtext>的使用示例:

<template>
    <div>
        <richtext @itemclick="listener" style="color:red;text-overflow:ellipsis">
            <span>link</span>
            <a >
                <image style="width:150; height:150" src="https://img.alicdn.com/tps/i2/TB1hRb1IXXXXXX3XVXXXQaP.pXX-87-87.jpeg" pseudo-ref="22"></image>
                <span style="font-size:42;color:#FF5400;">TAOBAO</span>
            </a>
            <image style="width:300; height:300" src="http://www.fresher.ru/manager_content/images2/kadry-veka/big/2-1.jpg" pseudo-ref="23"></image>
            <span>繼承Transition繼承Transition繼承Transition繼承Transition繼承Transition繼承Transition繼承Transition繼承Transition繼承Transition</span>
        </richtext>
    </div>
</template>

<script>
    module.exports = {
        methods: {
            listener: function (foo) {
                var modal = weex.requireModule('modal');
                modal.toast({
                'message': 'My pseudoRef is'+foo.pseudoRef,
                'duration': 3
                });
            }
        }
    }
</script>

<style>
.logo{width: 50;height: 50;}
.title{font-size:42; color: #FF5400;}
</style>

到此我們的weex組件就學(xué)習(xí)完了,下一篇博客我們來一起看看weex實(shí)際頁面的編寫。一套代碼在Android和Ios上都是生效。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容