这几天忙着做组件化了,博客晚了些更新,说到组件化,我近期也会用博客来介绍的。上一篇博客我们讲的是weex的内置组件,那么这篇博客我们学习最后四个组件,下篇博客我们就会讲到世界weex页面的编写了。这篇博客我们来学习<waterfall>、
<video>、<web>和<richtext>。weex的组件真的是要好好学牢固的,很多人可能会觉得定义太多记不住,没关系,在后续实际页面的编写上我们还是会温习的。
1.<waterfall>
的简介和使用
<waterfall>
组件是提供瀑布流布局的核心组件。瀑布流,又称瀑布流式布局是比较流行的一种页面布局,视觉表现为参差不齐的多栏布局。随着页面滚动条向下滚动,这种布局还可以不断加载数据块并附加至当前尾部。
<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>
:用于定义列表中的子列表项,类似于 HTML 中的 ul 之于 li。Weex 会对<cell>
进行高效的内存回收以达到更好的性能。 -
<header>
:当<header>
到达屏幕顶部时,吸附在屏幕顶部。 -
<refresh>
:用于给列表添加下拉刷新的功能。 -
<loading>
:<loading>
用法与特性和<refresh>
类似,用于给列表添加上拉加载更多的功能。
<waterfall>的
属性
-
show-scrollbar :
[可选]
可选值为 true/ false,默认值为 true。控制是否出现滚动条。[H5无效]
-
column-count:
[可选]
描述瀑布流的列数-
auto
: 意味着列数是被其他属性所决定的(比如 column-width) -
<integer>
: 最佳列数,column-width 和 column-count 都指定非0值, 则 column-count 代表最大列数。
-
-
column-width :
[可选]
描述瀑布流每一列的列宽-
auto
: 意味着列宽是被其他属性所决定的(比如 column-count) -
<length>
: 最佳列宽,实际的列宽可能会更宽(需要填充剩余的空间), 或者更窄(如果剩余空间比列宽还要小)。 该值必须大于0
-
-
column-gap:
[可选]
列与列的间隙. 如果指定了normal
。 -
left-gap:
[可选]
左边cell和列表的间隙. 如果未指定 。 -
right-gap:
[可选]
右边cell和列表的间隙. 如果未指定。
<waterfall>的
事件
支持所有通用事件:
- click:用于监听点击事件。(例如:一般绑定于子组件之上触发跳转)。
- longpress:用于监听长按事件(一般绑定于子组件之上例如:手机淘宝猜你喜欢瀑布流,长按可删除您不感兴趣的商品)。
- appear:用于监听子组件出现事件(一般绑定于子组件之上例如:监听最后一个元素出现,加载新的数据)
- disappear:用于监听子组件滑出屏幕事件(一般绑定于子组件之上)
<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 组件用于在页面中嵌入视频内容。
<video>的子组件
text
是唯一合法的子组件。
<video>的属性
- src, string. 内嵌的视频指向的URL。
-
play-status, string. 可选值为
play
|pause
,用来控制视频的播放状态,play
或者pause
,默认值是pause
。 -
auto-play, boolean. 当页面加载初始化完成后,用来控制视频是否立即播放,默认值是
false
。 - poster, string, v0.18+ & iOS. 指定视频首图的图片链接。
-
controls, string, v0.19+. 可选值为
controls
|nocontrols
,控制视频播放组件是否显示回放控制面板,默认会显示,当指定为nocontrols
时不显示回放控制面板。
<video>的事件
- start 当 playback 的状态是 Playing 时触发。
- pause 当 playback 的状态是 Paused 时触发。
- finish 当 playback 的状态是 Finished 时触发。
- fail 当 playback 状态是 Failed 时触发。
<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 属性指定的网页内容。
<web>
可以使 H5 与 Native 元素相结合。
- 您可以整个页面铺满 Web 页面(快速兼容您之前的 H5 页面)
- 可以使用 Web 和其他 Weex 组件合成复杂页面
- 使用 Web 组合出多种效果(设置透明背景的 H5 页面,灵活配置各类 H5 活动资讯)
<template>
<web src="https://www.taobao.com/"></web>
</template>
<script></script>
<style></style>
特别注意事项
-
<web>
不支持任何嵌套的子组件。 -
<web>
必须指定 width 和 height 的样式属性,否则将不起作用。 - 您可以使用 webview module来控制
<web>
。
<web>的属性
src是要加载的网页内容的 URL。建议指定线上真实存在的 URL 地址。
<web>的事件
支持 appear 和 disappear 事件同时支持:
-
pagestart 会在 Web 页面开始加载时调用。
事件对象:
-
url
: {String} 当前 Web 页面的 URL。
-
-
pagefinish 会在 Web 页面完成加载时调用。
事件对象:
-
url
: {String} 当前 Web 页面的 URL。 -
canGoBack
: {Boolean} 当前 Web 页面是否可以回退。 -
canGoForward
: {Boolean} 当前 Web 页面是否可以前进。 -
title
: {String} 当前 Web 页面的标题(仅限 iOS 平台)。
-
error 会在 Web 页面加载失败时调用。
receivedtitle 会在 Web 页面的标题发生改变时调用(仅限 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>的简介
富文本组件可以内嵌 <span>``<a>``<image>
。同时它也支持 <span>``<a>``<image>
的嵌套。
只有 <span>
, <a>
and <image>
可以包含在 <richtext>
标签里。<span>
and <a>
会被显示为 display:inline
,而 <image>
会被显示为 display:inline-block
。
<richtext>
的子节点分两种类型。
-
<span>
and<a>
可以再包含孩子节点。 -
<image>
不能再包含孩子节点。
富文本组件内部树形结构不能超过255层,超过的层会被忽略。
注意事项
-
<a>
标签在 iOS 上恒定为color:blue
蓝色样式,它孩子节点也会被应用为该样式,见下面样例。Android 上无此限制。 -
<image>
标签必须指定width
和height
. - 在图片下载完成前,
<image>
会保持空白状态,目前不支持显示占位图。 - 富文本组件自身不能嵌套。
-
<span>
,<a>
和<richtext>
- 可以被继承
- color
- font-family
- font-size
- font-style
- font-weight
- line-height
- 不可被继承
- background-color
- 可以被继承
-
<span>
- 可以被继承
- text-decoration: none | underline | line-through, 默认值是 none
- 可以被继承
-
<richtext>
- 不可被继承
- lines: 最大行数,必须为正数。
- 不可被继承
-
<image>
- 不可被继承
- width
- height
- 不可被继承
- 通用事件 支持所有通用事件。
-
itemclick. 只有
img
和span
标签可能触发,触发时机是:-
img
标签:-
img
被点击时没有任何父节点是a
- 如果第一个条件不满足,Weex 会尝试打开
a
标签指定的链接。 -
img
的 pseudo-ref 会作为参数传回来。
-
-
span
标签:
-
a
标签中的span
被点击 - 并且所在的
a
标签的href被指定为"click://"(这个条件iOS端强要求,Android端并不要求) - a标签设置了pseudo-ref。此时itemclick事件会被触发,并且携带pseudo-ref的值。
<richtext>的使用示例:
<template>
<div>
<richtext @itemclick="listener" style="color:red;text-overflow:ellipsis">
<span>link</span>
<a href="http://t.cn?_wx_tpl=http://g.tbcdn.cn/ali-wireless-h5/res/0.0.16/hello.js">
<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组件就学习完了,下一篇博客我们来一起看看weex实际页面的编写。一套代码在Android和Ios上都是生效。