目录
1. 内置组件
2. 自定义组件
<template>
组件
</template>
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>
网友评论