目录
1,主要属性
1.1,hover-class
1.2,hover-stop-propagation
1.3,hover-start-time、hover-stay-time
1.4,拒绝300毫秒延迟
2,示例代码与最佳实践
2.1,使用hover-class定义按钮状态
2.2,使用view实现flex布局
2.2.1,justify-content的值
2.2.2,align-items的值
2.2.3,flex-wrap的值
2.2.4,align-content的值
2.2.5,flex-direction的值
3,相关问题
3.1,如何把view上的内容绘制在画布上?
文 / 石桥码农
字符16612,阅读13分钟,实践30分钟
view
是最基础的,也是微信小程序第一个公布的容器组件。所谓容器组件,就像 HTML
里的 div
标签一样,是为容纳其它组件而存在的。它本身也可以有一些自己的样式,因为它本身也可以有样式,但它最重要的功能,是布局。
这个容器组件的主要属性有:
指定按下去的样式类。当 hover-class="none"
时,或者没有设置这个属性时,没有点击态效果。单击后,当松开手指时,组件恢复变化前的状态。
代码:
<view hover-class="bc_red" class="section__title">flex-direction: row</view>
效果:
当按下去时,view
容器应用了bc_red
样式,松开后,恢复如初。
hover-class
这个属性名称具有一定迷惑性。在HTML
开发中,mouseHover
事件指是的鼠标悬停于某页面元素之上时触发的事件,mouseDown
才是鼠标按下去的事件。这里hover-class
这个属性名称,从功能上来看,命名为tap-down-class
或press-down-class
更为合适。
指定是否阻止本节点的父及以上节点出现点击态。默认为false
,不阻止。
阻止节点出现点击态,什么意思呢?
代码:
<!-- 阻止父节点出现hover状态 --><view hover-class="bc_red" class="section__title"> flex-direction: row <view hover-stop-propagation hover-class="bc_green" class="section__title"> child view </view></view>
两个view
容器,里面那个view
使用了hover-stop-propagation
属性。由于它是一个布尔属性,只要写上属性,不填写属性值也代表真。如果写属性值的话,还要使用布尔值绑定:
hover-stop-propagation={{true}}
直接写"true",是字符串,是不行的。
效果:
从效果看,子容器有点击态,父容器没有,虽然父容器也设置了hover-class
属性。
官方文档中关于这个属性是这样描述的:“指定是否阻止本节点的祖先节点出现点击态”,什么是“点击态”?既然是点击态,把属性名称全名为hover-stop-propagation
就不妥当嘛,此处应该命名为press-stop-propagation
更为合适。
现在我们看另外一个问题,通过事件跟踪,验证一下hover-stop-propagation
属性的作用。
wxml
代码:
<!-- 阻止父节点出现hover状态 --><view id="parentView" bindtap="onTap" hover-class="bc_red" class="section__title"> flex-direction: row <view id="childView" bindtap="onTap" hover-stop-propagation hover-class="bc_green" class="section__title"> child view </view></view>
在父子容器同时绑定了tap
事件。为方便追踪,为父子容器分别分配了一个id
。父容器id
为parentView
,子容器id
为childView
。
js:
onTap(e){ console.log(e.target)}
单击一次,输出:
{id: "childView", offsetLeft: 20, offsetTop: 64, dataset: {…}}{id: "childView", offsetLeft: 20, offsetTop: 64, dataset: {…}}
为什么输出两次?单击一次,为什么触发两次tap
事件?
这是因为每个事件都有捕捉、目标与冒泡三个阶段,在view
视图容器上使用bind
绑定的事件,默认会在目标与冒泡两个阶段派发事件,一个是本身派发的,一个是子内容冒泡派发的。
冒泡事件会继续向上传递。hover-stop-propagation
属性就是阻止冒泡事件向上传递的。当设置该属性后,父容器即parentView
,便不会触发onTap
的执行,这是我们在输出中只看到childView
的原因。
那么怎么样可以让view
的tap
事件只触发一次呢?
可以使用catch
绑定事件函数。catch
与bind
的作用相同,与 bind
不同的是, catch
会阻止事件向上冒泡。
代码:
<!-- 阻止父节点出现hover状态,阻止冒泡 --><view id="parentView" bindtap="onTap" hover-class="bc_red" class="section__title"> flex-direction: row <view id="childView" catchtap="onTap" hover-stop-propagation hover-class="bc_green" class="section__title"> child view </view></view>
与前面代码只有一处不同,就是将子容器的bindtap
改为了catchtap
。
读者朋友们可以自行测试一下,改用catch
绑定事件函数后,onTap
函数单击一次执行一次。
hover-start-time
是按住后多久出现点击态,hover-stay-time
是手指松开后点击态保留多长时间,单位均是毫秒。没有特殊说明,微信小程序中所有属性中的时间单位都是毫秒。
这两个属性的设置说明,在view
容器组件内部,有人掐表做了定时。当touchstart
事件发生时,开始计时,到达hover-start-time
时,应用hover-class
样式;当touchstart
结束,即startend
事件发生时,开始hover-stay-time
倒计时,时间到则移除hover-class
样式。
设置hover-start-time
属性,是为了方便开发者控制hover-class
样式出现的时机。在测试中发现,但凡在view
上单击一下,很正常的速度单击,不需要悬停,也会出现hover-class
样式的应用。50ms
是极短的时间,但是在计算机的微观世界却是一个极长的时间,这个时间已经足以包裹一次系统单击事件。
单击事件不是一个点事件,也是一个跨一定时间段的物理现象。在mac
系统上,设置里有一个地方可以改变单击事件的跟踪速度。
改变这个跟踪速度后,在微信开发者工具模拟器中的tap
事情也受其影响。如果你把跟踪速度调整到快的一侧,单击时只是轻轻慢慢地一按,系统是不会触发单击事件的。
300
毫秒延迟我们知道,当延迟超过100
毫秒时,用户就会感觉到明显的卡顿。但是在移动设备上,特别在苹果的Safari
浏览器上,我们不得不忍受300
毫秒的延时。
这是为什么呢?
乔本斯在发布会上演示过这样一个功能,对于一个Safari
浏览器打开的网页:
在右边内容区快速双击,苹果会帮助我们准确定位到文章的主体内容,并将其放大:
这个功能很酷。
但是有一个问题,如果用户不小心在双击时单击了一个链接,这让软件怎么处理呢?是马上跳转,还是等待用户的另一个单击以判断是不是双击事件?
苹果采用的是第二种方式,所有Safafi
中的链接都要延迟300
毫秒,如果用户没有发出第二个单击事件,再跳转链接。这导致苹果手机中的Html5
页面都非常卡顿。
但是微信小程序没有这个问题,hover-start-time
的默认时间是50ms
,只需要50ms
甚至更短就可以触发单击事件,微信小程序已经打破了300
毫秒迟延的魔咒。
如果有人问你,使用微信小程序开发相比Html5
开发,有什么优势?没有单击延迟就是在体验上一个很大的优势。
有一个问题留给读者朋友们思考,hover-start-time
这个属性的值,最小可以设置为多少?设置为1
毫秒可以吗,为什么?这个问题在之前的推文中提到过。
使用hover-class
,必使用hover-stop-propagation
属性。
代码:
<view hover-stop-propagation hover-class="bc_green"></view>
这是为了避免父容器受子容器影响。
在自定义用于触发单击的按钮时,hover-class
特别有用。一般按钮有两种状态:常态与按下的状态。使用hover-class
正好可以定义按下的状态。
hover-class
定义按钮状态wxml
代码:
<!-- 距形按钮 --><view class="section"> <button type="default" class="btn" plain hover-class="rect-btn__hover_btn"><icon type="success_no_circle" size="26px"></icon>完成</button></view><!-- 圆形按钮 --><view class="section"> <button hover-class="circle-btn__hover_btn"><icon type="success" size="80px"></icon></button></view><!-- 普通按钮 --><view class="section"> <button hover-class="rect-btn__hover_btn" type="primary">完成</button></view>
三个按钮均是基于button
组件改造的。button
是组件,同时实际上也是容器,所以在button
上面也可以应用hover-class
属性。
wxss
代码:
.btn{ display: flex; align-items: middle; padding: 8px 50px 8px; border: 1px solid #b2b2b2; background-color: #f2f2f2;}.circle-btn__hover_btn { opacity: 0.8; transform: scale(0.95, 0.95);}.rect-btn__hover_btn { position: relative; top: 3rpx; left: 3rpx; box-shadow: 0px 0px 8px rgba(175, 175, 175, .2) inset;}
我们看看这三个类样式都有什么作用。
.btn
是普通的自定义按钮样式。flex
与align-items
是为了实现文本与图标的横向对齐。#b2b2b2
是符合微信设计规范的按钮边框色,#f2f2f2
是按钮背景色。
transform
使圆形按钮在单击时缩小0.05
。按钮单击时微微缩小,这是从Flash
交互时代传承下来的体验技巧。
#b2b2b2
做为边框色,rgba(175, 175, 175, .2)
是其做为rgba
格式的20%
透明形式,将它作为方形按钮按下状态的内阴影颜色,这也是符合微信颜色设计规范的。box-shadow
这个样式用于定义组件的内阴影。
运行效果:
view
实现flex
布局view
容器组件最大的作用,就是实现ui
布局。最常用的是flex
布局,基本所有常见的布局都可以用它实现。flex
布局指将display
样式设置为flex
,再加以其它相关的样式实现的布局。
关于flex
布局有三个十分重要的样式:
justify-content
:调整内容,主轴方向的排列方式align-items
:对齐元素,侧轴方向的对齐方式align-content
:对齐多行内容,侧轴方向多行排列方式以默认的flex-direction
为row
来看,即从左到右为主轴,自上而下为侧轴。
在这种情况下,justify-content
管制的是元素在x
方向的排列策略;align-items
管制的是主轴上排列的元素,在侧轴方向,即y
方向上的对齐方式;align-content
管制的是当出现多行以后,多行内容在侧轴方向上,即y
轴方向上的排列策略。
这三个属性很不好记,一时记住了,过一段时间用的时候可能还要查文档。可以这样辅助记住:
x
轴为主轴的情况下,即flex-direction
为row
,justify
单词的意思为「调整」,css
样式text-align
有一个值是justify
,意思是左右横向对齐,这里的justify
也是横向调整的意思。至于content
,它比items
的字面宽,更能代表行。justify-content
,就是align-items
。既然justify-content
负责的是横向调整,那么align-items
负责的就是纵向对齐。align-content
,结合align-items
与justify-content
记忆。align
指的是纵向对齐,content
指的是行,那么align-content
指的就是多行的纵向排列方式。flex-direction
为row
时,横向就是主轴,纵向就是侧轴。下面分别看一下这三个样式的作用。
justify-content
的值flex-start:主轴起点对齐,默认值
wxml
代码:
<view class="section"> <view class="section__title">justify-content:flex-start</view> <view class="flex-wrp" style="flex-direction:row;;justify-content:flex-start"> <view class="flex-item bc_green">1</view> <view class="flex-item bc_red">2</view> <view class="flex-item bc_blue">3</view> </view></view>
运行效果:
元素向主轴的起点看齐。与flex-start
对应的值是flex-end
。
flex-end:主轴结束点对齐
wxml
代码:
<view class="section"> <view class="section__title">justify-content:flex-end</view> <view class="flex-wrp" style="flex-direction:row;justify-content:flex-end"> <view class="flex-item bc_green">1</view> <view class="flex-item bc_red">2</view> <view class="flex-item bc_blue">3</view> </view></view>
运行效果:
元素在主轴方向上向尾部看齐。
center:在主轴中居中对齐
wxml
代码:
<view class="section"> <view class="flex-wrp" style="flex-direction:row;justify-content:center"> <view class="flex-item bc_green">1</view> <view class="flex-item bc_red">2</view> <view class="flex-item bc_blue">3</view> </view></view>
运行效果:
有空隙往首尾放,居中看齐。
space-between:向首尾看齐,相当于align-text
的justify
效果
wxml
代码:
<view class="section"> <view class="flex-wrp" style="flex-direction:row;justify-content:space-between"> <view class="flex-item bc_green">1</view> <view class="flex-item bc_red">2</view> <view class="flex-item bc_blue">3</view> </view></view>
运行效果:
两端的子元素靠向父容器两端,其他子元素之间的间隔相等。
space-around:元素之间的间隔,与与父容器之间的间隔相同
wxml
代码:
<view class="section"> <view class="flex-wrp" style="flex-direction:row;justify-content:space-around"> <view class="flex-item bc_green">1</view> <view class="flex-item bc_red">2</view> <view class="flex-item bc_blue">3</view> </view></view>
运行效果:
在视图效果上两边间隔较多一点,是因为外容器本身已经有了一个页边距。
align-items
的值stretch:填充整个容器,默认值
wxml
代码:
<view class="section"> <view class="flex-wrp" style="flex-direction:row;justify-content:space-around;align-items:stretch;"> <view class="flex-item bc_green">1</view> <view class="flex-item bc_red">2</view> <view style="height:auto;" class="flex-item bc_blue">3</view> </view></view>
bc_blue
样式本身有一个height
设定,为了让第三个子元素可以自由伸放,所以给它设置了height
为auto
。style
内嵌样式的优先级高于class
类样式。
运行效果:
第一个元素与第三个元素,均填充了整个父容器。在使用stretch
这个值时,容器高度取决于最高的那个,其它次高元素必须在高度上可以自由伸缩,才可以发挥作用。不可以有height
、min-height
等样式束缚。
flex-start:侧轴的起点对齐
wxml
代码:
<view class="section"> <view class="flex-wrp" style="flex-direction:row;justify-content:space-around;align-items:flex-start;"> <view class="flex-item bc_green">1</view> <view class="flex-item bc_red">2</view> <view style="height:auto;" class="flex-item bc_blue">3</view> </view></view>
运行效果:
默认x轴为主轴的情况下,效果就是顶部对齐了。
flex-end:侧轴的终点对齐
wxml
代码:
<view class="section"> <view class="flex-wrp" style="flex-direction:row;justify-content:space-around;align-items:flex-end;"> <view class="flex-item bc_green">1</view> <view class="flex-item bc_red">2</view> <view style="height:auto;" class="flex-item bc_blue">3</view> </view></view>
效果:
center:在侧轴中居中对齐
<view class="section"> <view class="flex-wrp" style="flex-direction:row;justify-content:space-around;align-items:center;"> <view class="flex-item bc_green">1</view> <view class="flex-item bc_red">2</view> <view style="height:auto;" class="flex-item bc_blue">3</view> </view></view>
效果:
baseline:以子元素的第一行文字对齐
前面的flex-start、flex-end、center
均是以元素本身所占的区域定位的,只有baseline
是以内部的文本定位的。
wxml
代码:
<view class="section"> <view class="section__title">以子元素的第一行文字对齐</view> <view class="flex-wrp" style="flex-direction:row;justify-content:space-around;align-items:baseline;"> <view class="flex-item bc_green">1</view> <view style="padding-top:30px;" class="flex-item bc_red">2</view> <view style="height:auto;line-height:150px;" class="flex-item bc_blue"><text>3</text></view> </view></view>
为了使baseline
的对齐效果明显,特意给第二、第三个元素设置了不一样的属性。
效果:
这个特性在设计一些以文本居中显示的ui
效果时很有用,无论文本周围有什么样的装饰效果,文本始终是在一条线上对齐的。
flex-wrap
的值flex-wrap
这个样式是为了控制主轴一行显示不了时候的换行策略的。它有三个值:
默认不换行的情况下,便于实现横向滚动效果。我们重点看一下换行的效果。
wxml
代码:
<view class="section"> <view class="section__title">多了换行</view> <view class="flex-wrp" style="flex-direction:row;justify-content:flex-start;align-items:baseline;flex-wrap:wrap;"> <view class="flex-item bc_green">1</view> <view class="flex-item bc_red">2</view> <view style="height:auto;" class="flex-item bc_blue">3</view> <view style="height:auto;" class="flex-item bc_blue">3</view> <view style="height:auto;" class="flex-item bc_blue">3</view> </view></view>
效果:
容器宽度不够的时候,会自动折到下一行显示;如果动态增加宽度,又会自动折回到上一行显示。这种特性方便实现一些瀑布流效果,不限定显示瀑布是几列,可以动态调整显示三列或四列。
align-content
的值通过前面justify-content、align-items
两个样式可以看出来,具有相同名称的值定义具有相似的含义。例如flex-start、flex-end、center
这三个值,它们表示的含义是近似的。
align-content
是为了控制多行在侧轴方向的排列方式,这个样式有这些值:
stretch、center、flex-start、flex-end、space-between、space-around
这些样式值在前面都出现过,在这里代表的含义也与前面类似。我们看一下space-between
的效果。
wxml
代码:
<view class="section"> <view class="section__title">多行侧轴的对齐方式</view> <view class="flex-wrp" style="flex-direction:row;justify-content:flex-start;align-items:baseline;flex-wrap:wrap;align-content:space-between;height:300px;"> <view class="flex-item bc_green">1</view> <view class="flex-item bc_red">2</view> <view style="height:auto;" class="flex-item bc_blue">3</view> <view style="height:auto;" class="flex-item bc_blue">3</view> <view style="height:auto;" class="flex-item bc_blue">3</view> <view style="height:auto;" class="flex-item bc_blue">3</view> <view style="height:auto;" class="flex-item bc_blue">3</view> </view></view>
运行效果:
三行之间的间隔是相等的。
flex-direction
的值还有一个样式在flex布局中不得不提:flex-direction
。它用于决定是x
轴,还是y
轴是主轴。默认情况下,也就是前面所讲的情况,是以x
轴为主轴的。
如果将flex-direction
的值改为column
,效果:
值得一提的是,如果将y轴定为主轴的话,决定元素横向排列的就不是justify-content
,而是align-items
了。
wxml
代码:
<view class="section"> <view class="section__title">flex-direction: column,align-items:center</view> <view class="flex-wrp" style="height: 300px;flex-direction:column;align-items:center;"> <view class="flex-item bc_green">1</view> <view class="flex-item bc_red">2</view> <view class="flex-item bc_blue">3</view> </view></view>
将侧轴方向上的对齐方式设定为center
。
效果:
x
轴为侧轴,所以三个元素表现为左右居中。
flex-direction
一共有四个值:
row-reverse
与row
相反,使元素在横向从右向左排列;column-reverse
与column
相反,使元素从向下向上排列。
view
上的内容绘制在画布上?view
目前不能直接转绘到画布上。如果想生成海报,一种可行的办法是:
wx.createCanvasContext
创建一个画布wx.canvasToTempFilePath
保存到本地,并获取一个临时图片路径wx.saveImageToPhotosAlbum
保存临时图片到本地相册有一个开源的小程序组件,封闭了通过json
数据绘制海报的功能:
//github.com/Kujiale-Mobile/Painter
在阶段代码中集合了这个组件,运行效果为:
单击「保存」就可以将海报保存到本地了。
阶段源码:
//git.weixin.qq.com/rxyk/weapp-practice/repository/archive.zip?ref=2.2-view
好了,我是石桥码农,这就是今天分享的内容,有什么问题欢迎留言讨论,也欢迎进群交流。
2020年4月7日
参考文献