question_uniapp/components/module/stable_waterfall_goods.vue

332 lines
8.2 KiB
Vue
Raw Permalink Normal View History

2025-06-06 03:08:19 +08:00
<template>
<view class="flow-box" :style="'height: ' + loadingTop + 'rpx'" ref="flow_box">
<view v-for="(item, index) in newList" :key="item.objId" class="item good_item" v-show="positionList[item.objId].imageLoad" :class="positionList[item.objId].right ? 'right' : 'left'" :style="{top: positionList[item.objId].top + 'rpx'}"
@click="onPageJump(item)">
<view class="goods_img">
<image :data-id="item.objId" @load="onImageLoad" :src="item.img" mode="widthFix"></image>
</view>
<view class="title">{{ item.name }}</view>
<view class="sell_well">
<text>热销</text>
</view>
<view class="info">
<text class="money"><text>{{ item.priceDiscount }}</text></text>
<text class="count">{{ item.numSales || 0 }}人购买</text>
</view>
</view>
</view>
</template>
<script>
const defaultData = {
// 起始距离rpx
startTop: 20,
// 除了图片和标题以外的高度rpx
contentHeight: 40 + 32 + 20 + 30 + 60,
// 瀑布流容器宽度rpx
viewWidth: 344,
// 标题可显示宽度rpx
titleWidth: 300,
// 标题文字大小rpx
titleSize: 28,
// 容器之间的间隔Y轴rpx
viewSpace: 20,
// 列表ID参数名称
idName: "objId",
// 列表标题参数名称
titleName: "name",
};
// 文字换行计算行数
function drawtext(text, maxWidth) {
let textArr = text.split("");
let len = textArr.length;
// 上个节点
let previousNode = 0;
// 记录节点宽度
let nodeWidth = 0;
// 文本换行数组
let rowText = [];
// 如果是字母,侧保存长度
let letterWidth = 0;
// 汉字宽度
let chineseWidth = defaultData.titleSize;
// otherFont宽度
let otherWidth = defaultData.titleSize / 2;
for (let i = 0; i < len; i++) {
if (/[\u4e00-\u9fa5]|[\uFE30-\uFFA0]/g.test(textArr[i])) {
if(letterWidth > 0){
if(nodeWidth + chineseWidth + letterWidth * otherWidth > maxWidth){
rowText.push({
type: "text",
content: text.substring(previousNode, i)
});
previousNode = i;
nodeWidth = chineseWidth;
letterWidth = 0;
} else {
nodeWidth += chineseWidth + letterWidth * otherWidth;
letterWidth = 0;
}
} else {
if(nodeWidth + chineseWidth > maxWidth){
rowText.push({
type: "text",
content: text.substring(previousNode, i)
});
previousNode = i;
nodeWidth = chineseWidth;
}else{
nodeWidth += chineseWidth;
}
}
} else {
if(/\n/g.test(textArr[i])){
rowText.push({
type: "break",
content: text.substring(previousNode, i)
});
previousNode = i + 1;
nodeWidth = 0;
letterWidth = 0;
}else if(textArr[i] == "\\" && textArr[i + 1] == "n"){
rowText.push({
type: "break",
content: text.substring(previousNode, i)
});
previousNode = i + 2;
nodeWidth = 0;
letterWidth = 0;
}else if(/[a-zA-Z0-9]/g.test(textArr[i])){
letterWidth += 1;
if(nodeWidth + letterWidth * otherWidth > maxWidth){
rowText.push({
type: "text",
content: text.substring(previousNode, i + 1 - letterWidth)
});
previousNode = i + 1 - letterWidth;
nodeWidth = letterWidth * otherWidth;
letterWidth = 0;
}
} else{
if(nodeWidth + otherWidth > maxWidth){
rowText.push({
type: "text",
content: text.substring(previousNode, i)
});
previousNode = i;
nodeWidth = otherWidth;
}else{
nodeWidth += otherWidth;
}
}
}
}
if (previousNode < len) {
rowText.push({
type: "text",
content: text.substring(previousNode, len)
});
}
return rowText.length;
}
let allow = true;
export default {
props: {
// 数据列表
list: {
type: Array,
default () {
return [];
}
}
},
data() {
return {
newList: [],
positionList: [],
loadingTop: 0,
leftHistoryTop: 0,
rightHistoryTop: 0
};
},
watch: {
// 数据
list: function(newVal, oldVal) {
this.newList = newVal;
this.getCalculationPosition();
}
},
methods: {
onPageJump(item) {
uni.navigateTo({
url: "/pages/home/shop/goodsDetail?objId=" + item.objId
})
},
refreshData() {},
// 节流
throttle(callback,time = 400){
if(!allow) return false;
allow = false;
setTimeout(function(){
allow = true;
callback();
},time);
},
// 获取数据位置信息 top,right
getCalculationPosition(){
let leftHistoryTop = defaultData.startTop;
let rightHistoryTop = defaultData.startTop;
let positionList = this.positionList;
this.newList.forEach((item,index) => {
let currentHeight = defaultData.contentHeight;
let positionItem = {};
// 查看是否有位置数据
if(positionList[item[defaultData.idName]]){
positionItem = positionList[item[defaultData.idName]];
}else {
positionList[item[defaultData.idName]] = {};
}
// 查看是否有图片高度数据,没有默认等宽
if(positionItem.imageHeight){
currentHeight += positionItem.imageHeight;
positionList[item[defaultData.idName]].imageLoad = true;
}else{
currentHeight += defaultData.viewWidth;
}
// 查看是否有标题高度数据,没有获取高度
if(positionItem.titleHeight){
currentHeight += positionItem.titleHeight;
}else{
let titleListLen = drawtext(item[defaultData.titleName], defaultData.titleWidth);
titleListLen = titleListLen > 2 ? 2 : titleListLen;
positionList[item[defaultData.idName]].titleHeight = titleListLen * (defaultData.titleSize + 6);
currentHeight += titleListLen * (defaultData.titleSize + 6);
}
if(leftHistoryTop > rightHistoryTop){
positionList[item[defaultData.idName]].top = rightHistoryTop;
positionList[item[defaultData.idName]].right = true;
positionList[item[defaultData.idName]].height = currentHeight;
rightHistoryTop += currentHeight + defaultData.viewSpace;
}else{
positionList[item[defaultData.idName]].top = leftHistoryTop;
positionList[item[defaultData.idName]].right = false;
positionList[item[defaultData.idName]].height = currentHeight;
leftHistoryTop += currentHeight + defaultData.viewSpace;
}
});
if(leftHistoryTop > rightHistoryTop){
this.loadingTop = leftHistoryTop;
}else{
this.loadingTop = rightHistoryTop;
}
this.positionList = positionList;
this.$forceUpdate();
},
// 图片加载完成
onImageLoad(e){
let id = e.currentTarget.dataset.id;
let scale = defaultData.viewWidth / e.detail.width;
let height = scale * e.detail.height;
if(this.positionList[id]){
this.positionList[id].imageHeight = height;
}else{
this.positionList[id] = {
imageHeight: height
}
}
// 图片加载完刷新位置节流一下,提升性能
this.throttle(() => {
this.getCalculationPosition();
}, 100);
}
}
};
</script>
<style scoped lang="scss">
@import '@/style/mixin.scss';
.flow-box {
position: relative;
color: #1a1a1a;
padding: 0 20rpx 0rpx 20rpx;
box-sizing: content-box;
}
.flow-box .left {
left: 20rpx;
}
.flow-box .right {
right: 20rpx;
}
.flow-box .good_item {
position: absolute;
width: 345rpx;
border: 2rpx solid #f9f9f9;
background: #fff;
border-radius: 20rpx;
overflow: hidden;
margin-bottom: 20rpx;
&.noMargin {
margin-right: 0;
}
.goods_img {
image {
width: 100%;
height: 345rpx;
}
}
.title {
margin: 20rpx 20rpx;
color: #333333;
font-size: 28rpx;
@include bov(2);
}
.sell_well {
display: flex;
padding: 0rpx 20rpx 20rpx 20rpx;
text {
height: 32rpx;
border-radius: 4rpx;
border: solid 2rpx $themeColor;
line-height: 28rpx;
padding: 0 15rpx;
font-size: 24rpx;
color: $themeColor;
}
}
.info {
width: 100%;
padding: 0rpx 20rpx 30rpx 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
.money {
font-size: 26rpx;
color: #ff3d3d;
display: flex;
align-items: center;
text {
font-size: 36rpx;
}
}
.count {
font-size: 24rpx;
color: #999;
}
}
}
</style>