目錄
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分鐘
1,主要屬性
view 是最基礎的,也是微信小程序第一個公佈的容器組件。所謂容器組件,就像 HTML 裡的 div 標籤一樣,是為容納其它組件而存在的。它本身也可以有一些自己的樣式,因為它本身也可以有樣式,但它最重要的功能,是佈局。
這個容器組件的主要屬性有:
1.1,hover-class
指定按下去的樣式類。當 hover-class="none" 時,或者沒有設置這個屬性時,沒有點擊態效果。單擊後,當鬆開手指時,組件恢復變化前的狀態。
代碼:
<code>flex-direction: row/<code>
效果:
當按下去時,view容器應用了bc_red樣式,鬆開後,恢復如初。
hover-class這個屬性名稱具有一定迷惑性。在HTML開發中,mouseHover事件指是的鼠標懸停於某頁面元素之上時觸發的事件,mouseDown才是鼠標按下去的事件。這裡hover-class這個屬性名稱,從功能上來看,命名為tap-down-class或press-down-class更為合適。
1.2,hover-stop-propagation
指定是否阻止本節點的父及以上節點出現點擊態。默認為false,不阻止。
阻止節點出現點擊態,什麼意思呢?
代碼:
<code> flex-direction: row child view /<code>
兩個view容器,裡面那個view使用了hover-stop-propagation屬性。由於它是一個布爾屬性,只要寫上屬性,不填寫屬性值也代表真。如果寫屬性值的話,還要使用布爾值綁定:
<code>hover-stop-propagation={{true}}/<code>
直接寫"true",是字符串,是不行的。
效果:
從效果看,子容器有點擊態,父容器沒有,雖然父容器也設置了hover-class屬性。
官方文檔中關於這個屬性是這樣描述的:“指定是否阻止本節點的祖先節點出現點擊態”,什麼是“點擊態”?既然是點擊態,把屬性名稱全名為hover-stop-propagation就不妥當嘛,此處應該命名為press-stop-propagation更為合適。
現在我們看另外一個問題,通過事件跟蹤,驗證一下hover-stop-propagation屬性的作用。
wxml代碼:
<code> flex-direction: row child view /<code>
在父子容器同時綁定了tap事件。為方便追蹤,為父子容器分別分配了一個id。父容器id為parentView,子容器id為childView。
js:
<code>onTap(e){ console.log(e.target)}/<code>
單擊一次,輸出:
<code>{id: "childView", offsetLeft: 20, offsetTop: 64, dataset: {…}}{id: "childView", offsetLeft: 20, offsetTop: 64, dataset: {…}}/<code>
為什麼輸出兩次?單擊一次,為什麼觸發兩次tap事件?
這是因為每個事件都有捕捉、目標與冒泡三個階段,在view視圖容器上使用bind綁定的事件,默認會在目標與冒泡兩個階段派發事件,一個是本身派發的,一個是子內容冒泡派發的。
冒泡事件會繼續向上傳遞。hover-stop-propagation屬性就是阻止冒泡事件向上傳遞的。當設置該屬性後,父容器即parentView,便不會觸發onTap的執行,這是我們在輸出中只看到childView的原因。
那麼怎麼樣可以讓view的tap事件只觸發一次呢?
可以使用catch綁定事件函數。catch與bind的作用相同,與 bind 不同的是, catch 會阻止事件向上冒泡。
代碼:
<code> flex-direction: row child view /<code>
與前面代碼只有一處不同,就是將子容器的bindtap改為了catchtap。
讀者朋友們可以自行測試一下,改用catch綁定事件函數後,onTap函數單擊一次執行一次。
1.3,hover-start-time、hover-stay-time
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事情也受其影響。如果你把跟蹤速度調整到快的一側,單擊時只是輕輕慢慢地一按,系統是不會觸發單擊事件的。
1.4,拒絕300毫秒延遲
我們知道,當延遲超過100毫秒時,用戶就會感覺到明顯的卡頓。但是在移動設備上,特別在蘋果的Safari瀏覽器上,我們不得不忍受300毫秒的延時。
這是為什麼呢?
喬本斯在發佈會上演示過這樣一個功能,對於一個Safari瀏覽器打開的網頁:
在右邊內容區快速雙擊,蘋果會幫助我們準確定位到文章的主體內容,並將其放大:
這個功能很酷。
但是有一個問題,如果用戶不小心在雙擊時單擊了一個鏈接,這讓軟件怎麼處理呢?是馬上跳轉,還是等待用戶的另一個單擊以判斷是不是雙擊事件?
蘋果採用的是第二種方式,所有Safafi中的鏈接都要延遲300毫秒,如果用戶沒有發出第二個單擊事件,再跳轉鏈接。這導致蘋果手機中的Html5頁面都非常卡頓。
但是微信小程序沒有這個問題,hover-start-time的默認時間是50ms,只需要50ms甚至更短就可以觸發單擊事件,微信小程序已經打破了300毫秒遲延的魔咒。
如果有人問你,使用微信小程序開發相比Html5開發,有什麼優勢?沒有單擊延遲就是在體驗上一個很大的優勢。
有一個問題留給讀者朋友們思考,hover-start-time這個屬性的值,最小可以設置為多少?設置為1毫秒可以嗎,為什麼?這個問題在之前的推文中提到過。
2,示例代碼與最佳實踐
使用hover-class,必使用hover-stop-propagation屬性。
代碼:
<code>/<code>
這是為了避免父容器受子容器影響。
在自定義用於觸發單擊的按鈕時,hover-class特別有用。一般按鈕有兩種狀態:常態與按下的狀態。使用hover-class正好可以定義按下的狀態。
2.1,使用hover-class定義按鈕狀態
wxml代碼:
<code> /<code>
三個按鈕均是基於button組件改造的。button是組件,同時實際上也是容器,所以在button上面也可以應用hover-class屬性。
wxss代碼:
<code>.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;}/<code>
我們看看這三個類樣式都有什麼作用。
.btn是普通的自定義按鈕樣式。flex與align-items是為了實現文本與圖標的橫向對齊。#b2b2b2是符合微信設計規範的按鈕邊框色,#f2f2f2是按鈕背景色。
transform使圓形按鈕在單擊時縮小0.05。按鈕單擊時微微縮小,這是從Flash交互時代傳承下來的體驗技巧。
#b2b2b2做為邊框色,rgba(175, 175, 175, .2)是其做為rgba格式的20%透明形式,將它作為方形按鈕按下狀態的內陰影顏色,這也是符合微信顏色設計規範的。box-shadow這個樣式用於定義組件的內陰影。
運行效果:
2.2,使用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時,橫向就是主軸,縱向就是側軸。
下面分別看一下這三個樣式的作用。
2.2.1,justify-content的值
flex-start:主軸起點對齊,默認值
wxml代碼:
<code> justify-content:flex-start 1 2 3 /<code>
運行效果:
元素向主軸的起點看齊。與flex-start對應的值是flex-end。
flex-end:主軸結束點對齊
wxml代碼:
<code> justify-content:flex-end 1 2 3 /<code>
運行效果:
元素在主軸方向上向尾部看齊。
center:在主軸中居中對齊
wxml代碼:
<code> 1 2 3 /<code>
運行效果:
有空隙往首尾放,居中看齊。
space-between:向首尾看齊,相當於align-text的justify效果
wxml代碼:
<code> 1 2 3 /<code>
運行效果:
兩端的子元素靠向父容器兩端,其他子元素之間的間隔相等。
space-around:元素之間的間隔,與與父容器之間的間隔相同
wxml代碼:
<code> 1 2 3 /<code>
運行效果:
在視圖效果上兩邊間隔較多一點,是因為外容器本身已經有了一個頁邊距。
2.2.2,align-items的值
stretch:填充整個容器,默認值
wxml代碼:
<code> 1 2 3 /<code>
bc_blue樣式本身有一個height設定,為了讓第三個子元素可以自由伸放,所以給它設置了height為auto。style內嵌樣式的優先級高於class類樣式。
運行效果:
第一個元素與第三個元素,均填充了整個父容器。在使用stretch這個值時,容器高度取決於最高的那個,其它次高元素必須在高度上可以自由伸縮,才可以發揮作用。不可以有height、min-height等樣式束縛。
flex-start:側軸的起點對齊
wxml代碼:
<code> 1 2 3 /<code>
運行效果:
默認x軸為主軸的情況下,效果就是頂部對齊了。
flex-end:側軸的終點對齊
wxml代碼:
<code> 1 2 3 /<code>
效果:
center:在側軸中居中對齊
<code> 1 2 3 /<code>
效果:
baseline:以子元素的第一行文字對齊
前面的flex-start、flex-end、center均是以元素本身所佔的區域定位的,只有baseline是以內部的文本定位的。
wxml代碼:
<code> 以子元素的第一行文字對齊 1 2 3 /<code>
為了使baseline的對齊效果明顯,特意給第二、第三個元素設置了不一樣的屬性。
效果:
這個特性在設計一些以文本居中顯示的ui效果時很有用,無論文本週圍有什麼樣的裝飾效果,文本始終是在一條線上對齊的。
2.2.3,flex-wrap的值
flex-wrap這個樣式是為了控制主軸一行顯示不了時候的換行策略的。它有三個值:
- nowrap:不換行,默認值
- wrap:換行
- wrap-reverse:換行,第一行在最下面
默認不換行的情況下,便於實現橫向滾動效果。我們重點看一下換行的效果。
wxml代碼:
<code> 多了換行 1 2 3 3 3 /<code>
效果:
容器寬度不夠的時候,會自動折到下一行顯示;如果動態增加寬度,又會自動折回到上一行顯示。這種特性方便實現一些瀑布流效果,不限定顯示瀑布是幾列,可以動態調整顯示三列或四列。
2.2.4,align-content的值
通過前面justify-content、align-items兩個樣式可以看出來,具有相同名稱的值定義具有相似的含義。例如flex-start、flex-end、center這三個值,它們表示的含義是近似的。
align-content是為了控制多行在側軸方向的排列方式,這個樣式有這些值:
<code>stretch、center、flex-start、flex-end、space-between、space-around/<code>
這些樣式值在前面都出現過,在這裡代表的含義也與前面類似。我們看一下space-between的效果。
wxml代碼:
<code> 多行側軸的對齊方式 1 2 3 3 3 3 3 /<code>
運行效果:
三行之間的間隔是相等的。
2.2.5,flex-direction的值
還有一個樣式在flex佈局中不得不提:flex-direction。它用於決定是x軸,還是y軸是主軸。默認情況下,也就是前面所講的情況,是以x軸為主軸的。
如果將flex-direction的值改為column,效果:
值得一提的是,如果將y軸定為主軸的話,決定元素橫向排列的就不是justify-content,而是align-items了。
wxml代碼:
<code> flex-direction: column,align-items:center 1 2 3 /<code>
將側軸方向上的對齊方式設定為center。
效果:
x軸為側軸,所以三個元素表現為左右居中。
flex-direction一共有四個值:
- row:從左到右的水平方向為主軸,是默認值
- row-reverse:從右到左的水平方向為主軸
- column:從上到下的垂直方向為主軸
- column-reverse:從下到上的垂直方向為主軸
row-reverse與row相反,使元素在橫向從右向左排列;column-reverse與column相反,使元素從向下向上排列。
3,相關問題
3.1,如何把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日
參考文獻
- 凌徵:"300 毫秒點擊延遲的來龍去脈".[Online]Available://thx.github.io/mobile/300ms-click-delay(2020).
- "view".[Online]Available://developers.weixin.qq.com/miniprogram/dev/component/view.html(2020).
這是《2020年小程序開發實踐》專欄的一部分:
www.yishulun.com/post/DwHiQLxx2/