美文网首页跨平台
weex入门之组件

weex入门之组件

作者: 平安喜乐698 | 来源:发表于2018-10-19 14:53 被阅读198次
目录
    1. 内置组件
    2. 自定义组件
<template>
组件
</template> 
1. 内置组件
  1. 布局

<div>

容器组件

<div>
</div>

注意:
    1、作为容器(建议控制在 10 层以内;一般作为根容器)
    2、在 native 中不可滚动

<scroller>

滚动组件

<scroller class="scroller">
    <div class="row" v-for="(name, index) in rows" :ref="'item'+index">
      <text class="text" :ref="'text'+index">{{name}}</text>
    </div>
</scroller>

<script>
  const dom = weex.requireModule('dom')
  export default {
    data () {
      return {
        rows: []
      }
    },
    created () {
      for (let i = 0; i < 30; i++) {
        this.rows.push('row ' + i)
      }
    },
    methods: {
      goto10 (count) {
滚动至特定位置
        const el = this.$refs.item10[0]
        dom.scrollToElement(el, {})
        dom.scrollToElement(el, { offset: 0 })
重置loadmore(当loadmore后数据没有发生变化,再次滚动到末尾时默认不在触发loadmore事件。可以通过重置来改变)
        dom.resetLoadmore() 
      },
    }
  }
</script>


属性
  show-scrollbar {boolean}
    是否出现滚动条,true默认值、false
  scroll-direction {string}
    滚动方向,horizontal、vertical默认值。
    样式属性 flex-direction 定义了 scroller 的布局方向,两个方向必须一致。
    横向滚动  scroll-direction:horizontal 、 flex-direction: row
    纵向滚动  scroll-direction:vertical 、 flex-direction: column。
  loadmoreoffset {number}
    触发 loadmore 事件所需要的垂直偏移距离(设备屏幕底部与页面底部之间的距离)。默认值为 0。
  offset-accuracy {number} 0.11+
    控制onscroll事件触发的频率,默认值为10px。
    注意,值越小会越提高滚动事件采样的精度,但越降低页面性能。


事件
  loadmore  v0.5+
    滚动到底部时触发
  scroll  0.11+
    滚动时触发(默认10px,即列表每滚动10px触发一次)
    事件event对象属性:
      contentSize {Object}          列表内容尺寸
      width {number}                列表内容宽度
      height {number}               列表内容高度
      contentOffset {Object}        列表的偏移尺寸
      x {number}                    x轴上的偏移量
      y {number}                    y轴上的偏移量
  scrollstart、scrollend 0.17+
    开始、结束滚动时触发。当前的内容高度和列表偏移会在callback中返回
  支持所有通用事件



说明:
    1、子组件:除了支持任意类型Weex组件,还支持<refresh>添加下拉刷新、<loading>上拉加载



注意:
    1、嵌套的 <list>/<scroller> 必须是不同的方向

<list>

纵向滚动列表组件(类似iOS的UITableView)

  <list class="list" @loadmore="fetch" loadmoreoffset="10">
   <header>
      <text class="banner">HEADER</text>
    </header>
    <cell class="cell" v-for="num in lists">
      <div class="panel">
        <text class="text">{{num}}</text>
      </div>
    </cell>
  </list>

  <script>
  const modal = weex.requireModule('modal')
  const LOADMORE_COUNT = 4

  export default {
    data () {
      return {
        lists: [1, 2, 3, 4, 5]
      }
    },
    methods: {
      fetch (event) {
        modal.toast({ message: 'loadmore', duration: 1 })

        setTimeout(() => {
          const length = this.lists.length
          for (let i = length; i < length + LOADMORE_COUNT; ++i) {
            this.lists.push(i + 1)
          }
        }, 800)
      }
    }
  }
  </script>



子组件
    只能包括以下四种组件或是 fix 定位的组件,其他形式的组件将不能被正确的渲染
    1、<cell>
    Weex 会对 <cell> 进行高效的内存回收以达到更好的性能
    2、header  0.6.1+
    悬浮在顶部(滚动到顶部时)
    3、<refresh>
    添加下拉刷新
    4、<loading>
    添加上拉加载

属性
    1、show-scrollbar {boolean}
    是否出现滚动条, true默认值、 false。
    2、loadmoreoffset {number}
    触发 loadmore 事件所需要的垂直偏移距离(设备屏幕底部与 <list> 底部之间的距离),默认值为 0
    3、offset-accuracy {number} 0.11+
    控制onscroll事件触发的频率,默认值为10px
    越小越提高滚动事件采样的精度,但越降低页面性能。
    4、pagingEnabled {boolean}
    是否以页为滚动单位,true、false(默认)

事件
    1、loadmore 0.5+
    滚动到底部时触发
    2、scroll 0.11+
    滚动时触发,事件的默认抽样率为10px
    事件中 event 对象属性:
      contentSize {Object}:列表的内容尺寸
      width {number}: 列表内容宽度
      height {number}: 列表内容高度
      contentOffset {Object}: 列表的偏移尺寸
      x {number}: x轴上的偏移量
      y {number}: y轴上的偏移量

方法(参考上面的scroller)
    1、scrollToElement(node, options)
    2、resetLoadmore() 

注意:
    1、嵌套的 <list>/<scroller> 必须是不同的方向
    2、<list> 为根节点时无需设置高度,但是内嵌 <list> 高度必须可计算,可以使用 flex 或 postion 将 <list> 设为一个响应式高度(例如全屏显示), 也可以显式设置 <list> 组件的 height 样式。

<cell>

    Weex 会对 <cell> 进行高效的内存回收以达到更好的性能
    支持所有 Weex 的组件作为它的子组件
    只能作为list、waterfall、recycler的子组件


属性
    1、keep-scroll-position {boolean}
    插入数据后是否保持上次滚动的位置

支持所有通用事件

注意:
    1、<cell> 本身是一个容器,其布局由 <list> 进行管理,你不能给 <cell> 设定flex值。 <cell>的宽度等于父组件 <list> 的宽度,并且 <cell> 高度自适应,指定 margin 样式也不起作用。

<waterfall>

瀑布流布局组件

子组件
    仅支持cell、header、refresh、 loading 、fixed-position 组件.

支持所有通用事件

属性
  show-scrollbar 
    是否出现滚动条。 true默认值、false
  column-width 
    每一列的宽度
    auto: 列宽由其他属性决定(比如 column-count)
    <length>: 最佳列宽,实际的列宽可能会更宽(需要填充剩余的空间) 或更窄(如果剩余空间比列宽还要小)。 该值必须大于0
  column-count
    列数
    auto: 列数由其他属性决定(比如 column-width)
    <integer>: 最佳列数,column-width 和 column-count 都指定非0值, 则 column-count 代表最大列数。
  column-gap
    列与列的间隙. 如果指定了 normal ,则对应 32.
  left-gap
    左边cell和列表的间隙. 如果未指定 ,则对应 0
  right-gap
    右边cell和列表的间隙. 如果未指定,则对应 0
完整示例

<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="header" ref="header" v-if="showHeader">
      <div class="banner">
       <image class="absolute" src="https://gw.alicdn.com/tps/TB1ESN1PFXXXXX1apXXXXXXXXXX-1000-600.jpg" resize="cover"></image>
       <div class="bannerInfo">
          <image class="avatar" src="https://gw.alicdn.com/tps/TB1EP9bPFXXXXbpXVXXXXXXXXXX-150-110.jpg" resize="cover"></image>
          <text class="name">Adam Cat</text>
          <div class="titleWrap">
            <text class="title">Genius</text>
          </div>
        </div>
        <div class="bannerPhotoWrap">
          <image class="bannerPhoto" v-for="photo in banner.photos" :src="photo.src"></image>
        </div>
      </div>
    </header>
    <header class="stickyHeader" >
      <div v-if="stickyHeaderType === 'none'" class="stickyWrapper">
        <text class="stickyText">Sticky 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: 128;
    width: 750;
    flex-direction: row;
    align-items: center;
    justify-content: center;
  }
  .refreshText {
    color: #888888;
    font-weight: bold;
  }
  .indicator {
    color: #888888;
    height: 40;
    width: 40;
    margin-right: 30;
  }
  .absolute {
  position: absolute;
  top: 0px;
  width: 750;
  height: 377;
}
  .banner {
    height: 377;
    flex-direction: row;
  }
  .bannerInfo {
    width:270;
    align-items: center;
    justify-content: center;
  }
  .avatar {
    width: 148;
    height: 108;
    border-radius: 54;
    border-width: 4;
    border-color: #FFFFFF;
    margin-bottom: 14;
  }
  .name {
    font-weight: bold;
    font-size:32;
    color:#ffffff;
    line-height:32;
    text-align:center;
    margin-bottom: 16;
  }
  .titleWrap {
    width: 100;
    height: 24;
    margin-bottom: 10;
    background-color: rgba(255,255,255,0.80);
    border-radius: 12;
    justify-content: center;
    align-items: center;
  }
  .title {
    font-size: 20;
    color: #000000;
  }
  .bannerPhotoWrap {
    width: 449;
    height: 305;
    background-color: #FFFFFF;
    border-radius: 12;
    margin-top: 35;
    padding: 12;
    flex-direction: row;
    justify-content: space-between;
    flex-wrap:wrap;
  }
  .bannerPhoto {
    width: 137;
    height: 137;
    margin-bottom: 6;
  }
  .stickyHeader {
    position: sticky;
    height: 94;
    flex-direction: row;
    padding-bottom:6;
  }
  .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:32;
    margin-right: 12;
  }
  .stickyImage {
    width: 64;
    height: 64;
    border-radius: 32;
  }

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

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

</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: false,
        scrollable: true,
        showStickyHeader: false,
        appearImage: null,
        disappearImage: null,
        stickyHeaderType: 'none',
        // fixedRect:'',
        banner: {
          photos: [
            {src:'https://gw.alicdn.com/tps/TB1JyaCPFXXXXc9XXXXXXXXXXXX-140-140.jpg'},
            {src:'https://gw.alicdn.com/tps/TB1MwSFPFXXXXbdXXXXXXXXXXXX-140-140.jpg'},
            {src:'https://gw.alicdn.com/tps/TB1U8avPFXXXXaDXpXXXXXXXXXX-140-140.jpg'},
            {src:'https://gw.alicdn.com/tps/TB17Xh8PFXXXXbkaXXXXXXXXXXX-140-140.jpg'},
            {src:'https://gw.alicdn.com/tps/TB1cTmLPFXXXXXRXXXXXXXXXXXX-140-140.jpg'},
            {src:'https://gw.alicdn.com/tps/TB1oCefPFXXXXbVXVXXXXXXXXXX-140-140.jpg'}
          ]
        },
        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) {
        // console.log(`${event.pullingDistance}`)
        if (event.pullingDistance < -64) {
          this.refreshText = '↑   release to refresh...'
        } else {
          this.refreshText = '↓   pull to refresh...'
        }
      }
    }
  }
</script>

<recycle-list>

一个新的列表容器,具有回收和复用的能力,可以大幅优化内存占用和渲染性能。

<recycle-list for="(item, i) in longList" switch="type">
  <cell-slot case="A">
    <text>- A {{i}} -</text>
  </cell-slot>
  <cell-slot case="B">
    <text>- B {{i}} -</text>
  </cell-slot>
</recycle-list>

const longList = [
  { type: 'A' },
  { type: 'B' },
  { type: 'B' },
  { type: 'A' },
  { type: 'B' }
]




属性
    1、for 属性
    支持如下两种写法:
        alias in expression
        (alias, index) in expression
    2、switch 属性
    配合 <cell-slot> 中的 case 和 default 属性一起使用
    如果省略了 switch 属性,则只会将第一个 <cell-slot> 视为模板,多余的模板将会被忽略

注意:
    1、只能使用 <cell-slot> 作为其直接子组件
    2、自定义组件<template> 标签添加 recyclable 属性,便可用在cell-slot中。
      <template recyclable>
        <div>
          <text>...</text>
        </div>
      </template>
    3、绑定属性或者文本时,仅支持表达式,不支持函数调用,也不支持使用 filter
    4、不能在 <cell-slot> 及其子组件里使用 <slot>,有冲突

<cell-slot>

<recycle-list>的直接子组件
只用来描述模板的结构

属性
    1、case 属性
    声明了当前模板的类型,一致则渲染并不再向下
    2、default 属性
    默认模板类型
    存在多个 default,则只会使用第一个默认模板
    3、key 属性
    可选属性,用于指定列表数据中可以作为唯一标识的键值,可以优化渲染性能


注意:
    1、只写 switch时使用第一个模板。
    2、写了 switch 时,case 和 default 必须写一个,否则该模板将会被忽略

<loading>

上拉加载组件
只能作为<scroller>、<list>、<hlist>、<vlist>、<waterfall> 的子组件

  <scroller class="scroller">
    <div class="cell" v-for="num in lists">
      <div class="panel">
        <text class="text">{{num}}</text>
      </div>
    </div>
    <loading class="loading" @loading="onloading" :display="loadinging ? 'show' : 'hide'">
      <text class="indicator-text">Loading ...</text>
      <loading-indicator class="indicator"></loading-indicator>
    </loading>
  </scroller>

      onloading (event) {
        modal.toast({ message: 'Loading', duration: 1 })
        this.loadinging = true
        setTimeout(() => {
          this.loadinging = false
        }, 2000)
      },

属性
    1、display
    必须成对出现
    display="show" :有loading-indicator则转菊花
    display="hide" :有loading-indicator则停止转菊花    
    2、支持所有通用属性

支持所有通用样式

事件
    1、loading
    当 <scroller>、<list>、<hlist>、<vlist>、<waterfall> 被上拉时触发

子组件
    支持任何子组件。其中 <loading-indicator>: 只能作为 <refresh> 和 <loading> 的子组件提供菊花转动效果。

<refresh>

下拉刷新组件
只能作为<scroller>、<list>、<hlist>、<vlist>、<waterfall> 的子组件


  <scroller class="scroller">
    <refresh class="refresh" @refresh="onrefresh" @pullingdown="onpullingdown" :display="refreshing ? 'show' : 'hide'">
      <text class="indicator-text">Refreshing ...</text>
      <loading-indicator class="indicator"></loading-indicator>
    </refresh>
    <div class="cell" v-for="num in lists">
      <div class="panel">
        <text class="text">{{num}}</text>
      </div>
    </div>
  </scroller>

      onrefresh (event) {
        modal.toast({ message: 'Refreshing', duration: 1 })
        this.refreshing = true
        setTimeout(() => {
          this.refreshing = false
        }, 2000)
      },
      onpullingdown (event) {
        console.log("dy: " + event.dy)
        console.log("pullingDistance: " + event.pullingDistance)
        console.log("viewHeight: " + event.viewHeight)
        console.log("type: " + type)
      }


子组件
    支持任何子组件。其中 <loading-indicator>: 只能作为 <refresh> 和 <loading> 的子组件

支持所有通用样式

属性
    1、display
    必须成对出现
    display="show" :有loading-indicator则转菊花
    display="hide" :有loading-indicator则停止转菊花    
    2、支持所有通用属性

事件
    1、refresh
    当 <scroller>、<list>、<hlist>、<vlist>、<waterfall> 被下拉时触发
    2、pullingdown
    当 <scroller>、<list>、<hlist>、<vlist>、<waterfall> 被下拉时触发
    事件event对象属性:
        dy:前后两次回调滑动距离的差值
        pullingDistance:下拉的距离
        viewHeight:refresh 组件高度
        type: “pullingdown” 字符串

其他

<a>

<a href="http://xxx.wx">
  <text>Jump</text>
</a> 

注意:
    1、待跳转页面必须是一个Weex页面

<image>

<image placeholder="" src="https://vuejs.org/images/logo.png" resize="cover"></image>

说明:
    placeholder:占位图
    src:图片路径
    cover:缩放模式(cover 可能留白/ contain可能超出 / stretch默认不按比例缩放)
注意:
    1、必须先在native 注册adapter或者 handler。
    2、<image> 必须指定宽度和高度。
    3、<image> 不支持内嵌子组件
-------------
     注册adapter


>>>>>>1、创建adapter

WeexImageDownloader.h

#import <Foundation/Foundation.h>
#import <WeexSDK/WeexSDK.h>
@interface WeexImageDownloader : NSObject<WXImgLoaderProtocol>
@end


WeexImageDownloader.m
#import "WeexImageDownloader.h"
#import <SDWebImage/SDWebImageManager.h>
@implementation WeexImageDownloader
- (id<WXImageOperationProtocol>)downloadImageWithURL:(NSString *)url imageFrame:(CGRect)imageFrame userInfo:(NSDictionary *)userInfo completed:(void(^)(UIImage *image,  NSError *error, BOOL finished))completedBlock
{
    if ([url hasPrefix:@"//"]) {
        url = [@"http:" stringByAppendingString:url];
    }
    return (id<WXImageOperationProtocol>)[[[SDWebImageManager sharedManager]imageDownloader]downloadImageWithURL:[NSURL URLWithString:url] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
    } completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
        if (completedBlock) {
            completedBlock(image, error, finished);
        }
    }];
}
@end

>>>>>>2、注册adapter
在AppDelegate.m文件didFinishLaunchingWithOptions中添加

[WXSDKEngine registerHandler:[WeexImageDownloader new] withProtocol:@protocol(WXImgLoaderProtocol)];
-------------
     保存图片到本地(save方法)

1、首先info.plist中添加NSPhotoLibraryAddUsageDescription、NSPhotoLibraryUsageDescription权限

2、
image 设置 ref="img" 
this.$refs['img'].save(function(result) {
  if (result.success) {
  } else {
    console.log(result.errorDesc)
  }
});

具体代码如下saveImg.vue:
-------------
<template>
  <div>
    <image class="img" ref="img" style="width: 300px; height: 300px;" src="https://developer.android.google.cn/studio/images/projects/cpp-project-view_2-2_2x.png">
    </image>
    
    <text class="button" value="SaveImg" type="primary" @click="save"></text>
  </div>
</template>

<style scoped>
  .button {
    font-size: 36;
    width: 200;
    color: #41B883;
    text-align: center;
    padding-top: 10;
    padding-bottom: 10;
    border-width: 2;
    border-style: solid;
    margin-right: 20;
    border-color: rgb(162, 217, 192);
    background-color: rgba(162, 217, 192, 0.2);
  }
  .img {
  margin-bottom: 20px;
  }
</style>

<script>
  var modal = weex.requireModule('modal')
  module.exports = {
    data: function () {
    },
    methods: {
      save: function () {
        this.$refs['img'].save(function(result) {
          modal.toast({ message: "Img sava " + result.success + ", " + result.errorDesc})
        });
      },
    }
  };
</script>
-------------
     事件(load图片加载完毕后调用)

    <image @load="onImageLoad" src="path/to/image.png"></image>
    <script>
      module.exports = {
        data : {
        },
        methods : {
          onload : function(e) {
            nativeLog(JSON.stringify(e))
            if (e.success){
              this.download = 'success'; 
            }
            this.size = e.size.naturalWidth + ',' + e.size.naturalHeight;
          },
        }
      }
    </script>


具体代码如下test.vue:
-------------
<template>
  <div>
    <div title = 'image-onload' :padding-body='0'>
      <div style='flex-direction:row'>
        <image class='mr-base' style="width: 300px;height: 300;" src="https://gw.alicdn.com/tps/TB1bEMYKXXXXXaLaXXXXXXXXXXX-360-388.png" @load="onload"></image>
      </div>
      <div>
        <text test-id='imgSize' style="font-size:30px">size : {{size}}</text>
        <text test-id='download' style="font-size:30">{{download}}</text>
      </div>
    </div>
  </div>
</template>
<script>
  module.exports = {
    data : {
      size:"-1,-1",
      download:'false'
    },
    methods : {
      onload : function(e) {
        nativeLog(JSON.stringify(e))
        if (e.success){
          this.download = 'success'; 
        }
        this.size = e.size.naturalWidth + ',' + e.size.naturalHeight;
      },
    }
  }
</script>
---------------
    base64、gif

    <image class="img" style="width: 96px; height: 96px;" src="data:image/png;base64,iVBORw0KGgoAAA...></image>
    <image class="img" style="width: 96px; height: 96px;" src="data:image/gif;base64,iVBORw0KGgoAAA...></image>
---------------
    在script中设置src后再加载图片


具体代码如下test.vue:
---------------
<template>
  <div class="wrapper">
    <div class="container" @click="loadImage">
      <image class="image" v-if="src" :src="src" resize="cover"></image>
      <text class="alt" v-else>Click to load image</text>
    </div>
  </div>
</template>

<script>
  export default {
    data () {
      return {
        src: ''
      }
    },
    methods: {
      loadImage () {
        if (!this.src) {
          this.src = 'https://cdn.dribbble.com/users/179241/screenshots/1829868/nerfwarrior_dribbble.png'
        }
      }
    }
  }
</script>

<style scoped>
  .wrapper {
    justify-content: center;
    align-items: center;
  }
  .container {
    width: 602px;
    height: 602px;
    border-width: 2px;
    border-color: #DDD;
    background-color: #F5F5F5;
    justify-content: center;
    align-items: center;
  }
  .image {
    width: 600px;
    height: 600px;
  }
  .alt {
    font-size: 50px;
    color: #AAA;
  }
</style>

<text>

<text class="text" lines="3">Weex</text>


注意:
    1、<text> 只能包含文本、文本也只能放在<text>中
    2、可以使用 {{}} 标记插入变量值作为文本内容
    3、<text>不支持子组件
    4、font-size默认值为32、lines默认值是0不限制行数
---------------
    添加自定义字体

<input>

接收用户输入
    <input type="text" placeholder="Input Text" class="input" :autofocus=true value="" return-key-type="done" @change="onchange" @input="oninput"/>
    <input type="password"/>

      onchange: function (event) {
        this.txtChange = event.value;
        console.log('onchange', event.value);
      },
      onreturn: function (event) {
        this.txtReturnType = event.returnKeyType;
        console.log('onreturn', event.type);
      },
      oninput: function (event) {
        this.txtInput = event.value;
        console.log('oninput', event.value);
      },
      focus: function () {
        this.$refs['input1'].focus();
      },
      blur: function () {
        this.$refs['input1'].blur();
      },
      setRange: function() {
        console.log(this.$refs["inputselection"]);
        this.$refs["inputselection"].setSelectionRange(2, 6);
      },
      getSelectionRange: function() {
        console.log(this.$refs["inputselection"]);
        var self = this;
        this.$refs["inputselection"].getSelectionRange(function(e) {
          self.txtSelection = e.selectionStart +'-' + e.selectionEnd;
        });
      }


  type
    控件的类型:text(默认)、date、datetime、email、 password、tel、time、url、number 
  value
    默认内容
  placeholder
    提示文本(不能有回车或换行)
  disabled 
    是否支持输入(通常 click 事件在 disabled 控件上是失效的)。
  autofocus
    是否在页面加载时控件自动获得输入焦点。
  maxlength、max-length 
    输入的最大长度
  return-key-type
    键盘返回键的类型:defalut、go、next、search、send、done。
  singleline
    是否只允许单行
  lines
    输入内容的最大行数
  max
    控制当type属性为date时选择日期的最大时间,格式为yyyy-MM-dd
  min
    控制当type属性为date时选择日期的最小时间,格式为yyyy-MM-dd

样式
    1、placeholder-color {color}:placeholder 字符颜色。默认值是 #999999
    2、支持以下伪类:
    active、focus、disabled、enabled
    3、支持所有通用样式

事件
    1、input
    输入字符改变后触发。
    事件中 event 对象属性:
      value: 触发事件的组件;
      timestamp: 事件发生时的时间戳,仅支持Android。
    2、change
    输入完成时触发。通常在 blur 事件之后
    3、focus
    获取焦点时触发。
    4、blur
    失去焦点时触发。
    5、return
    键盘点击返回键时触发
    事件中 event 对象属性:
      returnKeyType: 事件发生时的返回键类型。
      value: 触发事件的组件的文本;
    6、通用事件
    支持以下通用事件:
        longpress、appear、disappear

方法
    1、focus() v0.9+
    获取焦点
    2、blur() v0.9+
    失去焦点
    3、setSelectionRange(selectionStart,selectionEnd) v0.11+
    设置文本选区
    4、getSelectionRange(callback[selectionStart,selectionEnd]) v0.11+设置文本选区
    设置文本选区
    5、setTextFormatter(params)v0.18+
    设置一组对输入内容实时格式化的规则。
    params {object}:格式化规则,包含以下参数:
      formatRule {regexp}:格式化匹配的正则表达式
      formatReplace {string}:格式化匹配后用于替换的内容
      recoverRule {regexp}:从格式化后的内容还原原始内容的正则表达式
      recoverReplace {string}:还原原始内容时用于替换的内容

注意:
    1、不支持 click 事件。通过监听 input 或 change 来代替 click 事件。
    2、不支持子组件
    3、目前不支持 this.$el(id).value = '' 这种方式改写 input value。只支持在 <input> 组件的 input、change 事件中改写

<textarea>

   <textarea> 等价于 多行的 <input>


<textarea class="textarea" @input="oninput" @change="onchange" @focus="onfocus" @blur="onblur"></textarea>

    oninput (event) {  其他事件也一样
        console.log('oninput:', event.value)
        modal.toast({
          message: `oninput: ${event.value}`,
          duration: 0.8
        })
      }


除了支持text组件的所有属性,还支持
    rows {number}
    指定组件的高度,默认值是 2

支持 <input> 支持的所有的事件

支持以下伪类:
    active
    focus
    disabled
    enabled

事件
    1、input
    输入字符改变后触发。
        事件中 event 对象属性:
          value: 触发事件的组件;
          timestamp: 事件发生时的时间戳。
    2、change
    当用户输入完成时触发。通常在 blur 事件之后。
    3、focus
    获取焦点时触发。
    4、blur
    失去焦点时触发。
    5、通用事件
    不支持 click 事件。 请监听 input 或 change 事件代替。
    支持以下通用事件:
        longpress
        appear
        disappear

注意:
    1、textarea 不支持子组件

<web>

<web ref="webview" style="width: 730px; height: 500px" src="https://www.baidu.com" @pagestart="onPageStart" @pagefinish="onPageFinish" @error="onError" @receivedtitle="onReceivedTitle"></web>


后退、前进、刷新
        var webview = weex.requireModule('webview');
        webview.goBack(this.$refs.webview);
        webview.goForward(this.$refs.webview);
        webview.reload(this.$refs.webview);

事件
      onPageStart: function(e) {
        this.pagestart = e.url;
      },
      onPageFinish: function(e) {
        this.pagefinish = e.url;
        this.canGoBack = e.canGoBack;
        this.canGoForward = e.canGoForward;
        if (e.title) {    // title仅限 iOS 平台
          this.title = e.title;
        }
      },
      onError: function(e) {
        this.error = url;
      },
      onReceivedTitle: function(e) {
        this.title = e.title;
      }
事件说明:
    1、pagestart       页面开始加载时调用
    2、pagefinish      页面完成加载时调用
    3、error           页面加载失败时调用
    4、receivedtitle   页面的标题发生改变时调用(仅限 Android 平台)
    5、公共事件仅支持appear 和 disappear


注意:
    1、<web> 不支持子组件
    2、必须指定 width 和 height 的样式属性,否则将不起作用
    3、可以使用webview内置模块来控制组件

<video>

<video class="video" src="http://.../1.mp4" autoplay controls
      @start="onstart" @pause="onpause" @finish="onfinish" @fail="onfail"></video>

src {string}
    视频URL
play-status {string}
    控制视频的播放状态, play、pause默认值
auto-play {boolean}
    控制(当页面加载初始化完成后)视频是否立即播放,true、false默认值

事件
      onstart (event) {    开始播放
        this.state = 'onstart'
      },
      onpause (event) {    暂停播放
        this.state = 'onpause'
      },
      onfinish (event) {    结束播放
        this.state = 'onfinish'
      },
      onfail (event) {      播放失败
        this.state = 'onfinish'
      }


注意:
    1、<text> 是唯一合法的子组件

<slider>

轮播图组件

    <slider class="slider" interval="3000" auto-play="true">
      <div class="frame" v-for="img in imageList">
        <image class="image" resize="cover" :src="img.src"></image>
      </div>
      <indicator class="indicator"></indicator>
    </slider>

  <script>
  export default {
    data () {
      return {
        imageList: [
          { src: 'https://c.jpg'},
          { src: 'https://c.jpg'},
          { src: 'https://c.jpg'}
        ]
      }
    }
  }
  </script>

属性
    1、auto-play {boolean}
    是否自动轮播。true、false默认
    重置 loadmore 相关的 UI,值不一样就会重置。
    2、interval {number}
    切换时间间隔,值为毫秒数。
    当 auto-play 值为 true 时生效。
    3、infinite {boolean}
    是否循环播放,true默认、false。
    4、offset-x-accuracy {number}0.11+:
    控制onscroll事件触发的频率,默认值为10px。
    越小越提高滚动事件采样的精度,但越降低页面性能。
    5、show-indicators {boolean}
    是否显示indicator
    6、index {number}
    指定当前显示的slider页面
    7、scrollable {boolean}
    是否可以通过滑动手势来切换slider页面
    8、keep-index {boolean}
    slider中的数据发生变化后是否保持变化前的页面index

事件
    1、change
    当轮播索引改变时触发。
    事件中 event 对象属性:
        index:展示的图片索引
        scroll 0.11+:列表发生滚动时将会触发该事件,事件的默认抽样率为10px
        offsetXRatio {number}:表示当前页面的偏移比例,取值范围为[-1, 1],负值表示向左侧滚动,正值向右。
    2、支持所有通用事件


注意:
    1、支持任意类型的 Weex 组件作为其子组件。其中<indicator> 用于显示轮播图指示器效果,只能作为slider组件的子组件使用。
    2、

<indicator>

实例如上slider

用于显示轮播图指示器效果(小圆点),只能作为slider组件的子组件使用
不支持任何子组件

私有样式,如下:
    1、item-color {color}
    未被选中时的颜色,默认值为 #CCCCCC
    2、item-selected-color {color}
    被选中时的颜色,默认值为 #444444
    3、item-size {number}
    指示点的半径,默认为 5px

支持所有通用事件

注意:
    1、<indicator> 的 position 不仅依赖 top、left、bottom 和 right 样式,同时会参考 width 和 height 样式。
       <indicator> 默认的宽高继承于 <slider>,如果 <slider> 未设置宽高,需要显式的给 <indicator> 设置宽高值。
    2、background-color 不推荐使用,建议使用 item-color 和 item-selected-color 代替。
2. 自定义组件

第一步:创建自定义组件

WXButton : WXComponent

-(instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance{
    
    self=[super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
    if(self){
        // 获取组件的指定属性值
        NSLog(@"%@",[WXConvert NSString:attributes[@"title"]]);
    }
    return self;
}

第二步:注册自定义组件

AppDelegate的didFinishLaunchingWithOptions方法中+

[WXSDKEngine registerComponent:@"weex-button" withClass:[WXButton class]];

第三步:vue文件中使用

<template>
      <div>
        <weex-button title="hello"></weex-button>
      </div>
</template>

相关文章

网友评论

    本文标题:weex入门之组件

    本文链接:https://www.haomeiwen.com/subject/zegyzftx.html