开发实战篇之九
前言
实战篇内容参考:
1、Lin Ui开源组件源码分析。https://doc.mini.talelin.com/
2、开发过程遇到问题。
1、grid格子组件
分析:grid
组件是一整个布局,为了使得布局具有灵活性,因此将grid
中的单元也抽象出来成为grid-item
组件。
1.1 grid骨架文件wxml
<view class="x-grid x-class" bind:tap="tapGrid">
<view
bind:tap="tapGridItem"
data-grid-index="{{item.index}}"
class="x-grid-item x-class-grid x-grid-class {{index%rowNum !== rowNum-1 &&(showBorder||showColBorder) ? 'side-grid':''}} {{(index<gridItems.length-(gridItems.length%rowNum||rowNum)) &&(showBorder||showRowBorder)? 'center-grid':''}}" wx:for="{{gridItems}}" wx:key="key"
style="min-width:{{100/rowNum}}%;"
>
<slot name="{{item.key}}"></slot>
</view>
</view>
为了在grid
布局中添加新组件,因此就在view
标签之间使用slot
插槽。为整体的grid
添加tapGrid
监听事件,每一个grid-item
添加tapGridItem
监听事件。
1.2 grid的js文件分析
编写跟grid-item的父子组件关系,并触发initGrids函数。
relations: {
'../grid-item/index': {
type: 'child',
// 关系建立在页面节点树时触发initGrids函数
linked() {
this.initGrids();
},
unlinked() {
this.initGrids();
}
},
},
// 初始化子组件数据,组件序号index、编号key、内容cell
initGrids() {
// 1、获取所有的子组件节点items
let items = this.getRelationNodes('../grid-item/index');
// 子节点个数不变 不需要更新
if (this.data.childNum === items.length) return;
// 2、js语法通过map 获取数据对象 数组
const gridItems = items.map((item, index) => {
item.setData({
index,
});
return {
index:index,
key: item.data.key,
cell: item.data.cell
};
});
// 页面渲染grid-item数据
this.setData({
gridItems: gridItems,
childNum: items.length
});
},
给grid整体和grid-item都添加监听事件,这样就能够提供自定义定制功能。
// 点击grid-item
// 获取当前排列的index,并以该index从gridItem[]中获取数据
// data-grid-index="{{item.index}}"
tapGridItem(e) {
// console.log(e.target.dataset);
const { gridIndex } = e.target.dataset;
this.setData({
currentIndex: gridIndex,
currentCell: this.data.gridItems[gridIndex].cell
});
},
// 点击grid整体
tapGrid() {
this.triggerEvent('gridtap', {
index: this.data.currentIndex,
cell: this.data.currentCell
}, { bubbles: true, composed: true });
// 重置当前item
this.setData({
currentIndex: -1,
currentCell: -1
});
}
1.3 grid-item的wxml文件分析
使用view
的hover-
属性,使用isHover
控制grid-item
的点击事件,添加tap事件“tapGridItem
”
<view
hover-class="{{isHover?'x-grid-item-hover':''}}"
hover-start-time="20"
hover-stay-time="50"
class="x-grid-item x-grid-item-class grid-item"
mut-bind:tap="tapGridItem"
>
<slot />
</view>
hover-class
决定点击事件时,对grid-item
进行半透明处理。
1.4 grid-item的wxss文件分析
每个grid-item添加布局
.grid-item {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
padding: 32rpx 16rpx;
box-sizing: border-box
}
.x-grid-item-hover {
/* 透明度0.5 */
opacity: .5
}
1.5 grid在页面中的使用
index.wxml
<x-grid x-class="grid" show-border="{{true}}">
<x-grid-item
bind:griditemtap="clickGrid" wx:for="{{grids1}}" wx:key="{{index}}" key="{{index}}" slot="{{index}}"
cell="{{item}}" >
<x-icon name="{{item.image}}" />
<view class="text">{{item.text}}</view>
</x-grid-item>
</x-grid>
index.js
clickGrid(event) {
// console.log(event);
wx.showToast({
title: event.detail.cell.text,
icon: 'none'
});
},
2、image-picker图片选择组件
选择需要的图片上传,可自定义上传图片的数量。
image-picker的wxml文件:
<!--components/image-picker/index.wxml-->
<!-- 图片 -->
<x-grid row-num="{{ size }}" x-class="x-class">
<x-grid-item wx:for="{{ urls }}" wx:key="index" key="{{ index }}" slot="{{ index }}" x-grid-item="x-grid-item">
<view class="item x-item-class" catchtap="onPreviewTap" data-index="{{ index }}">
<view class="close" data-index="{{ index }}" catchtap="onDelTap">
<x-icon name="close" color="#fff" size="22" x-class="close-icon" />
</view>
<image class="{{size === 3? 'img': 'min-img'}}" mode="{{ mode }}" src="{{newOrOld==='old'? item:item.url }}" />
</view>
</x-grid-item>
<!-- add 按钮 -->
<x-grid-item wx:if="{{ showBtn }}" x-grid-item="x-grid-item">
<!-- 自定义图片选择按钮 -->
<view class="item x-item-class {{size === 3? 'img': 'min-img'}}" catchtap="onAddTap" wx:if="{{ custom }}">
<slot></slot>
</view>
<view class="item x-item-class {{size === 3? 'img': 'min-img'}}" catchtap="onAddTap" wx:else>
<image class="add-icon" src="./image/add.png" />
</view>
</x-grid-item>
</x-grid>
思路主要是使用grid
格子布局将图片通过wx:for="{{ urls }}"
展示出来,然后有add
添加按钮,进行添加图片。
主要分析下preview
预览图片函数,使用urls
和cells
区分两种图片的链接,一种是字符串数组,一种是字符串对象数组。然后在组件初始化时,使用变量newOrOld
变量区分两种链接类型,预览时根据对cells
数组的判断,newOrOld
是否为old进行判断,然后将不同结构中存储的url放入previewImgList[]
数组中去,再调用wx.previewImage()
预览图片。
// 点击预览图片 preview
onPreviewTap(e) {
// 当前图片索引 data-index
const index = e.currentTarget.dataset.index;
// urls = 'http://...'类似的地址数组
const urls = this.data.urls;
let tempFilePath = '';
// 预览list是字符串数组
let previewImageList = [];
const newOrOld = this.data.newOrOld;
const cellsIsObject = Object.prototype.toString.call(this.data.cells) === '[object Object]';
// 第一个 if 是对 cells 的兼容处理
// 如果cells是object对象
if (cellsIsObject) {
const cells = this.data.cells;
tempFilePath = cells[index].url;
for (let i = 0; i < cells.length; i++) {
previewImageList.push(cells[i].url);
}
} else if (newOrOld === 'old') {
// old urls是字符串数组
tempFilePath = this.data.urls[index];
previewImageList = this.data.urls;
} else {
// cells中不是object,newOrOld === 'new'...
// url中存的url是object对象
tempFilePath = this.data.urls[index].url;
for (let i = 0; i < urls.length; i++) {
previewImageList.push(urls[i].url);
}
}
// 因为wx预览图片需要当前图片和预览图片组,所以每次点击预览时生成previewImageList
let detail = {
index, // 下标
current: urls[index], // 当前显示图片的http链接
all: urls // 需要预览的图片http链接列表
};
let option = {};
if (this.data.preview === true) {
wx.previewImage({
current: tempFilePath, // 当前显示图片的http链接
urls: previewImageList // 需要预览的图片http链接列表
});
}
// 传递给父组件 当前显示的图片等数据
this.triggerEvent('imgpreview', detail, option);
},