接上文:微信小程序開發02-小程序基本介紹css
咱們今天先來實現這個彈出層:html
以前這個組件是一個容器類組件,彈出層可設置載入的html結構,而後再設置各類事件便可,這種組件有一個特色:git
① 只提供Header部分以及容器部分github
② 容器部分的HTML結構由業務層提供web
③ 容器部分對應樣式由業務層提供小程序
咱們若是要在小程序中實現這類組件,意味着咱們須要往小程序中動態插入WXML結構,咱們這裏先作個demo,試試往動態插入WXML是否是可行微信小程序
1 this.setData({'wxml': ` 2 <my-component> 3 <view>動態插入的節點</view> 4 </my-component> 5 `});
小程序對應設置的數據進行了轉義,因此並不能動態解析,若是站在性能角度思考,不進行動態解析也不是錯誤的;另外一方面,一旦小程序能動態解析wxml,那麼可能會涌出各類花式用法,控制力會減低,那麼咱們這裏如何解決這個問題呢?微信
我想的是,直接將業務級wxml結構放到頁面裏面,隱藏起來,須要使用彈出層的時候,直接將之裝載進去,咱們來看看是否可行,咱們將咱們須要展現的結構放到一個模板當中:dom
1 <template name="searchbox"> 2 <my-component> 3 <view>動態組件部分</view> 4 </my-component> 5 </template>
而後,咱們在咱們主界面中載入模板:xss
1 <import src="mod.searchbox.wxml"/> 2 <view> 3 <my-component> 4 <!-- 這部份內容將被放置在組件 <slot> 的位置上 --> 5 <view>這裏是插入到組件slot中的內容</view> 6 </my-component> 7 </view> 8 <view> 9 <template is="searchbox" /> 10 </view>
主體結構放到頁面中,咱們傳入數據模型或者控制顯示便可,看起來是可行的,因而咱們先實現咱們基本的樣式,由於業務模塊的樣子應該由業務提供,因此對應樣式寫到index.wxss裏面:
1 .btn-primary { 2 background-color: #00b358; 3 color: #fff; 4 border: 0 none; 5 } 6 .btn, .btn-primary, .btn-secondary, .btn-sub { 7 line-height: 88rpx; 8 height: 88rpx; 9 padding: 0 20rpx; 10 display: inline-block; 11 vertical-align: middle; 12 text-align: center; 13 border-radius: 8rpx; 14 cursor: pointer; 15 font-size: 32rpx; 16 -webkit-box-sizing: border-box; 17 box-sizing: border-box; 18 } 19 .full-width { 20 width: 100%; 21 -webkit-box-sizing: border-box; 22 box-sizing: border-box; 23 } 24 25 26 .c-row { 27 width: auto; 28 display: -webkit-box; 29 -webkit-box-orient: horizontal; 30 -webkit-box-direction: normal; 31 -webkit-box-pack: justify; 32 -webkit-box-align: stretch; 33 -webkit-box-lines: single; 34 display: -webkit-flex; 35 -webkit-flex-direction: row; 36 -webkit-justify-content: space-between; 37 -webkit-align-items: strecth; 38 -webkit-align-content: flex-start; 39 -webkit-flex-wrap: nowrap; 40 padding: 20rpx 40rpx; 41 } 42 43 .c-span3 { 44 width: 25%; 45 -webkit-box-flex: 3; 46 -webkit-flex: 3 3 auto; 47 } 48 49 .c-span9 { 50 width: 75%; 51 -webkit-box-flex: 9; 52 -webkit-flex: 9 9 auto; 53 } 54 55 .search-line { 56 position: relative; 57 height: 96rpx; 58 line-height: 96rpx; 59 font-size: 30rpx; 60 font-weight: 600; 61 border-bottom: 1rpx solid #e6e6e6; 62 } 63 64 .search-line::after { 65 content: ""; 66 display: inline-block; 67 vertical-align: middle; 68 width: 20rpx; 69 height: 20rpx; 70 border-top: 4rpx solid #00b358; 71 border-right: 4rpx solid #00b358; 72 position: absolute; 73 right: 60rpx; 74 top: 50%; 75 margin-top: -4rpx; 76 -webkit-transform: rotate(45deg) translateY(-50%); 77 transform: rotate(45deg) translateY(-50%); 78 -webkit-box-sizing: border-box; 79 box-sizing: border-box; 80 } 81 82 .search-line-txt { 83 text-align: right; 84 padding-right: 60rpx; 85 overflow: hidden; 86 text-overflow: ellipsis; 87 white-space: nowrap; 88 }
1 <template name="searchbox"> 2 <view class="c-row search-line" data-flag="start"> 3 <view class="c-span3"> 4 出發</view> 5 <view class="c-span9 js-start search-line-txt"> 6 請選擇出發地</view> 7 </view> 8 <view class="c-row search-line" data-flag="arrive"> 9 <view class="c-span3"> 10 到達</view> 11 <view class="c-span9 js-arrive search-line-txt"> 12 請選擇到達地</view> 13 </view> 14 <view class="c-row " data-flag="arrive"> 15 <span class="btn-primary full-width js_search_list">查詢</span> 16 </view> 17 </template>
如此一來,咱們基本的彈出層樣式就七七八八了,這裏能夠看出一些特色:小程序與平時咱們的樣式差距不大,稍微改點就能用,甚至能直接通用;另外一方面,咱們也須要思考一個問題:公共部分的CSS該怎麼處理?其實我這裏須要解決的不僅是公共的樣式部分,還須要解決公共的組件部分。
我這裏想的是將全部公共部分的CSS放到一個全局的文件global.wxss中,而後在每一個業務級頁面import便可,因此咱們這裏須要造成一個公共的WXSS庫,這個與純web映射起來便可,咱們這裏便不深刻。
要提升開發效率的第一個前提就是要有足夠多的UI組件,小程序自己提供了一些定製化的組件,咱們仍然會用到的組件有:
① alert類彈出層
② loading類彈出層
③ 日曆組件
④ toast&message類提示彈出組件
⑤ 容器類組件
⑥ ......
以前的作法,是咱們將html實體和組件實現直接放到一塊兒,css放到全局global裏面去,如今小程序並不支持動態展現wxml,因此動態插入的方式行不通了,咱們須要將組件的wxml放到頁面裏面作預加載,這裏我想的是提供一個通用global.ui.wxml文件用以裝載全部的wxml實體,經常使用的組件咱們默認全局引入,咱們這裏先挑點軟柿子來捏,咱們先實現一個alert類彈出層組件。
咱們將原來彈出層類會用到的CSS所有翻譯爲WXSS,放入global.wxss中:
而後咱們每一個組件都會有一個固定的生命週期:建立->顯示->隱藏,這個生命週期是每一個組件都具備的特性,因此咱們這裏應該引入繼承概念實現組件,可是小程序官方提供的Components並無提供繼承概念,而是提供了behaviors概念,用以將組件間的公共部分處理掉,因此咱們這裏也使用behaviors,由於不能操做dom,咱們的組件抽象會變得相對簡單,不用記錄太多dom節點了,另外小程序的組件與咱們以前的「組件」從定義到使用上有很大的不一樣,以前咱們是以js做爲控制器,如今是以標籤wxml做爲控制器,根本沒有辦法在js中獲取實例,而小程序組件的生命週期並不包含顯示隱藏生命週期,因此他的組件和咱們覺得的組件有很大的不一樣
我思考了下爲何小程序中,js不能獲取組件的實例,這裏得出的結論是:
小程序中全部的WXML必須在頁面中進行預加載邏輯,不能動態插入DOM的方式插入WXML,因此小程序沒有提供組件實例給咱們控制
因此在小程序中想完成組件庫,那麼便只能把組件作標籤使用(並且是js不能獲取的標籤),而不是js組件,這樣會有效幫助咱們理解
咱們這裏嘗試實現一個遮蓋層的標籤(這裏開始不用組件這個詞,感受頗有歧義):
代碼很是簡單:
<view class="cm-overlay"></view>
.cm-overlay { background: rgba(0, 0, 0, 0.5); position: fixed; top: 0; right: 0; bottom: 0; left: 0; }
1 let LayerView = require('behavior-layer-view') 2 3 Component({ 4 behaviors: [LayerView], 5 6 data: { 7 myData: {} 8 }, 9 attached: function () { }, 10 methods: { 11 } 12 })
能夠看到,這個遮蓋層mask沒有什麼意義,並且通常來講mask也不會單獨存在,通常是一個組件(好比彈出層的loading)會包含一個遮蓋層,因此咱們這裏要改造下Mask的結構,讓他能夠裝載組件,咱們從js組件邏輯來講是mask應該是loading的一個實例,可是咱們站在標籤角度來講,他們兩個應該是獨立的:
1 <view class="cm-overlay"> 2 <slot></slot> 3 </view>
咱們這裏實現一個loading的組件(PS:CSS3動畫稍微要作點兼容調試):
1 .spinner { 2 width: 140rpx; 3 height: 140rpx; 4 position: fixed; 5 align-items: center; 6 display: flex; 7 top: 50%; 8 left: 50%; 9 margin-left: -70rpx; 10 margin-top: -70rpx; 11 } 12 13 .container1 > view, .container2 > view, .container3 > view { 14 width: 24rpx; 15 height: 24rpx; 16 background-color: #00b358; 17 border-radius: 100%; 18 position: absolute; 19 -webkit-animation: bouncedelay 1.2s infinite ease-in-out; 20 animation: bouncedelay 1.2s infinite ease-in-out; 21 -webkit-animation-fill-mode: both; 22 animation-fill-mode: both; 23 } 24 25 .spinner .spinner-container { 26 position: absolute; 27 width: 66%; 28 height: 66%; 29 top: 10%; 30 left: 10%; 31 } 32 33 .container2 { 34 -webkit-transform: rotateZ(45deg); 35 transform: rotateZ(45deg); 36 } 37 38 .container3 { 39 -webkit-transform: rotateZ(90deg); 40 transform: rotateZ(90deg); 41 } 42 43 .circle1 { top: 0; left: 0; } 44 .circle2 { top: 0; right: 0; } 45 .circle3 { right: 0; bottom: 0; } 46 .circle4 { left: 0; bottom: 0; } 47 48 .container2 .circle1 { 49 -webkit-animation-delay: -1.1s; 50 animation-delay: -1.1s; 51 } 52 53 .container3 .circle1 { 54 -webkit-animation-delay: -1.0s; 55 animation-delay: -1.0s; 56 } 57 58 .container1 .circle2 { 59 -webkit-animation-delay: -0.9s; 60 animation-delay: -0.9s; 61 } 62 63 .container2 .circle2 { 64 -webkit-animation-delay: -0.8s; 65 animation-delay: -0.8s; 66 } 67 68 .container3 .circle2 { 69 -webkit-animation-delay: -0.7s; 70 animation-delay: -0.7s; 71 } 72 73 .container1 .circle3 { 74 -webkit-animation-delay: -0.6s; 75 animation-delay: -0.6s; 76 } 77 78 .container2 .circle3 { 79 -webkit-animation-delay: -0.5s; 80 animation-delay: -0.5s; 81 } 82 83 .container3 .circle3 { 84 -webkit-animation-delay: -0.4s; 85 animation-delay: -0.4s; 86 } 87 88 .container1 .circle4 { 89 -webkit-animation-delay: -0.3s; 90 animation-delay: -0.3s; 91 } 92 93 .container2 .circle4 { 94 -webkit-animation-delay: -0.2s; 95 animation-delay: -0.2s; 96 } 97 98 .container3 .circle4 { 99 -webkit-animation-delay: -0.1s; 100 animation-delay: -0.1s; 101 } 102 103 @-webkit-keyframes bouncedelay { 104 0%, 80%, 100% { -webkit-transform: scale(0.0) } 105 40% { -webkit-transform: scale(1.0) } 106 } 107 108 @keyframes bouncedelay { 109 0%, 80%, 100% { 110 transform: scale(0.0); 111 -webkit-transform: scale(0.0); 112 } 40% { 113 transform: scale(1.0); 114 -webkit-transform: scale(1.0); 115 } 116 }
1 <ui-mask z-index="{{maskzIndex}}" ></ui-mask> 2 <view class="spinner" style="z-index: {{meIndex}}"> 3 <view class="spinner-container container1"> 4 <view class="circle1"></view> 5 <view class="circle2"></view> 6 <view class="circle3"></view> 7 <view class="circle4"></view> 8 </view> 9 <view class="spinner-container container2"> 10 <view class="circle1"></view> 11 <view class="circle2"></view> 12 <view class="circle3"></view> 13 <view class="circle4"></view> 14 </view> 15 <view class="spinner-container container3"> 16 <view class="circle1"></view> 17 <view class="circle2"></view> 18 <view class="circle3"></view> 19 <view class="circle4"></view> 20 </view> 21 </view>
1 const util = require('../utils/util.js'); 2 let LayerView = require('behavior-layer-view'); 3 4 Component({ 5 behaviors: [LayerView], 6 7 data: { 8 maskzIndex: util.getBiggerzIndex(), 9 meIndex: util.getBiggerzIndex() 10 }, 11 attached: function () { 12 console.log('loading') 13 }, 14 methods: { 15 } 16 })
index調用狀況:
1 <import src="./mod.searchbox.wxml" /> 2 3 <view> 4 <template is="searchbox" /> 5 <ui-loading></ui-loading> 6 </view>
咱們後續將完整的項目代碼放到github上去,這裏便繼續代碼了
因而,咱們開始添加事件了,這裏添加一個點擊遮蓋層關閉整個組件的功能,這裏有個問題是,咱們點擊遮蓋層事實上關閉的是遮蓋以及loading兩個標籤,而咱們這裏的isShow屬性便派上了用處,咱們如今page中設置下屬性:
<ui-loading is-show="{{isLoadingShow}}"></ui-loading>
1 onShow: function() { 2 this.setData({ 3 isLoadingShow: '' 4 }); 5 },
而後咱們改造mask以及loading添加事件:
1 <view class="cm-overlay" style="z-index: {{zIndex}}; display: {{isShow}}" bindtap="onTap"> 2 </view>
1 let LayerView = require('behavior-layer-view') 2 3 Component({ 4 behaviors: [LayerView], 5 6 data: { 7 myData: {} 8 }, 9 attached: function () { 10 console.log('mask') 11 }, 12 methods: { 13 onTap: function() { 14 this.triggerEvent('customevent', {}, {}) 15 } 16 } 17 })
1 <ui-mask z-index="{{maskzIndex}}" is-show="{{isShow}}" bindcustomevent="onMaskEvent"></ui-mask> 2 <view class="spinner" style="z-index: {{meIndex}}; display: {{isShow}}; "> 3 <view class="spinner-container container1"> 4 <view class="circle1"></view> 5 <view class="circle2"></view> 6 <view class="circle3"></view> 7 <view class="circle4"></view> 8 </view> 9 <view class="spinner-container container2"> 10 <view class="circle1"></view> 11 <view class="circle2"></view> 12 <view class="circle3"></view> 13 <view class="circle4"></view> 14 </view> 15 <view class="spinner-container container3"> 16 <view class="circle1"></view> 17 <view class="circle2"></view> 18 <view class="circle3"></view> 19 <view class="circle4"></view> 20 </view> 21 </view>
1 const util = require('../utils/util.js'); 2 let LayerView = require('behavior-layer-view'); 3 4 Component({ 5 behaviors: [LayerView], 6 7 data: { 8 maskzIndex: util.getBiggerzIndex(), 9 meIndex: util.getBiggerzIndex() 10 }, 11 attached: function () { 12 console.log('loading') 13 }, 14 methods: { 15 onMaskEvent: function (e) { 16 console.log(e); 17 this.setData({ 18 isShow: 'none' 19 }); 20 } 21 } 22 })
這個時候,當咱們點擊遮蓋層的時候,咱們整個組件便關閉了。
咱們今天花了不少功夫寫一個loading,發現小程序中的組件事實上是標籤,咱們無法使用js獲取到咱們「組件」的實例,因此使用上有很大的區別,可是什麼都不能阻礙咱們寫通用組件的決心,因而咱們明天來寫一些通用的組件庫,而且造成一個小程序的體系,這裏想的是有:
① 消息框
② toast提示
③ 日曆組件
④ 而後再作一個須要定位的氣泡組件