Home >WeChat Applet >Mini Program Development >Let's talk about how to implement a truncated waterfall flow component in a small program

Let's talk about how to implement a truncated waterfall flow component in a small program

青灯夜游
青灯夜游forward
2022-01-24 10:32:312654browse

How to implement a truncated waterfall flow component in a small program? The following article will introduce to you how to implement a WeChat applet that can cut off waterfall flow components. I hope it will be helpful to you!

Let's talk about how to implement a truncated waterfall flow component in a small program

Waterfall flow is a common layout method. There are many ways to implement it, such as dividing it into two columns directly, and then controlling the addition of elements in the left and right columns; there is another way It is to place both sides through absolute positioning. [Related learning recommendations: 小program development tutorial]

The waterfall flow to be introduced in this article is different from the conventional one, because the waterfall flow may be truncated in the middle:

Lets talk about how to implement a truncated waterfall flow component in a small program

For the above layout, it is not suitable to force the layout into two columns, so I used absolute positioning for layout. Since the height of the elements in the waterfall flow is not fixed, Therefore, I have to find a way to get the height of each element, and then determine whether the element should be placed on the entire row, on the left, or on the right.

First let's look at the implementation of the template part:

<view class="container" style="height:{{height}}px;">
	<view wx:for="{{list}}" wx:key="index" style="{{item.style}}" class="wrapper">
		<abstract item="{{item}}"/>
	</view>
</view>
<view wx:if="{{tmp}}" class="computed-zone">
	<view class="wrapper">
		<abstract item="{{tmp}}"/>
	</view>
</view>

The template is relatively simple, a container container, then loops the array, and renders a bunch of horizontally wrapper Container.

wrapper The container is an absolutely positioned wrapping element. wrapper The container needs to place components that need to be actually rendered. For greater flexibility, I render this The component is set as a virtual node. When using the component, you can specify the custom component that is actually rendered.

Because the wrapper element is absolutely positioned, we need to manually maintain the height of the entire container container.

The question here is, how do we get the height of the elements inside? The computed-zone in the template is to solve this problem. Before placing the elements into the array, we first render the elements in computed-zone and then use the WXML api to Get the actual rendering size of the element, so that we can know the actual rendered width and height of this element.

After having the rendering size information of each element, we need to confirm whether the element occupies the entire row or half of it:

  • If the rendering width of the element is the same as If the container is the same, then you can judge that this element fills the entire row. You need to set the wrapping container wrapper to the width of the entire row;

  • If it does not satisfy 1 Condition, then you need to place wrapper on the left or right based on the total height of the left and right elements.

After analysis, what needs to be written a little bit is to calculate the offset of wrapper and decide whether to put it on the left or right, or whether it will occupy the entire line. , the core code is implemented as follows:

{
	// 将 setData Promise 化,方便使用
	$setData(data) {
		return new Promise(resolve => {
			this.setData(data, () => {
				resolve();
			});
		});
	},
	// 获取元素的渲染尺寸
	getRect(item) {
		return this.$setData({
			tmp: item,
		}).then(() => {
			return new Promise((resolve, reject) => {
				const query = this.createSelectorQuery(); // 注意要使用 this,不要再使用 wx 前缀了
				query.select(&#39;.computed-zone .wrapper&#39;).boundingClientRect();
				query.exec(ret => {
					if (ret[0]) {
						resolve(ret[0]);
					} else {
						reject(&#39;not found dom!&#39;);
					}
				});
			});
		});
	},
	// 添加元素,内部使用
	addItem(item) {
		let tick = this.tick;
		return this.getRect(item).then(rect => {
			if (tick !== this.tick) {
				return Promise.reject(&#39;tick&#39;);
			}
			const { margin } = this.data;
			let { height, width } = rect;
			const windowWidth = this.sysInfo.windowWidth;
			let [ leftTotal, rightTotal ] = this.height; // leftTotal 左侧栏高度,rightTotal 右侧栏高度,
			let marginPx = this.sysInfo.getPx(margin);
			let style = &#39;&#39;;

			if (Math.abs(width - windowWidth) < 3) {
				// 占满屏幕宽度
				style = `left:0;top:${ Math.max(leftTotal, rightTotal) }px;width:100%;`;
				leftTotal = rightTotal = Math.max(leftTotal + height, rightTotal + height);
			} else if (rightTotal < leftTotal) {
				// 放入右边
				style = `right:${ marginPx }px;top:${ rightTotal }px;`;
				rightTotal += height;
			} else {
				// 放入左边
				style = `left:${ marginPx }px;top:${ leftTotal }px;`;
				leftTotal += height;
			}

			const { list = [] } = this.data;
			const targetKey = `list[${list.length}]`; // 利用直接操作数组下标的方式来触发数组修改,性能有很大提升
			this.height = [leftTotal, rightTotal]; // 记录最新的左右侧高度
			return this.$setData({
				[targetKey]: {
					data: item,
					style,
				},
				height: Math.max(leftTotal, rightTotal),
			});
		});
	},
	// 实际添加元素使用,自建Promise队列,保证顺序一致
	add(item) {
		let pending = this.pending || Promise.resolve();
		return this.pending = pending.then(() => {
			return this.addItem(item);
		}).catch(err => {
			console.error(err);
			this.pending = null;
			throw err;
		});
	},
	clear() {
		this.tick = tick++;
		this.height = [0, 0];
		this.pending = null;
		this.setData({
			list: [],
			height: 0,
		});
	},
}

When using this component, we cannot render elements directly by assigning arrays, but must use the component instance method add(item) way, because I implemented the queue, I can just loop add directly. If you care about the status, just determine whether the add operation of the last element is completed.

The waterfall flow achieved in this way is relatively flexible, but the performance consumption is not low. You need to obtain the actual rendering size of the elements one by one. If you want to support the resize of the window, the consumption is terrible. .

For students who need to see the details of the code, I have put the actual demo on Github and WeChat code snippets. Students in need can give it a try.

Based on the above model, it can actually be optimized to only render elements within the visible area, which can greatly improve the performance of waterfall flow. I hope that students who have time can improve it. I will fork and like it~o ( ̄▽ ̄)d

For more programming-related knowledge, please visit: Programming Video! !

The above is the detailed content of Let's talk about how to implement a truncated waterfall flow component in a small program. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:juejin.cn. If there is any infringement, please contact admin@php.cn delete