微信小程序:实现节点进度条的效果;正在完成的节点有动态循环效果;横向,纵向排列

参考说明

微信小程序实现流程进度功能 - 知乎

上面的为一个节点进度条的例子,但并不完整,根据上述代码,进行修改完善,实现其效果

横向效果

代码

wxml

<view class='order_process'>
  <view class='process_wrap' wx:for="{{processData}}" wx:key="index">
    <view class='process'>
      <view class='process_line' style="background:{{item.start}}"></view>
      <image class="process_icon {{item.icon === icon2 ? 'rotate-icon' : ''}}" src="{{item.icon}}"></image>
      <view class='process_line' style="background:{{item.end}}"></view>
    </view>
    <text class='process_name'>{{item.name}}</text>
  </view>
</view>

wxss

.order_process {
  display: flex;
  flex-wrap: nowrap;
  padding: 10rpx 10rpx 20rpx 10rpx;
  background-color: #fff;
}
.process_wrap {
  display: flex;
  flex-direction: column;
  flex: 1;
  align-items: center;
}
.process {
  display: flex;
  align-items: center;
  width: 100%;
}
.process_icon {
  width: 35rpx;
  height: 35rpx;
}
.process_line {
  background: #eff3f6;
  flex: 1;
  height: 5rpx;
}
.process_name {
  font-size: 24rpx;
}
/* 定义旋转动画 */
@keyframes rotate360 {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
/* 应用到 .rotate-icon 类 */
.rotate-icon {
  animation: rotate360 5s linear infinite; /* 5秒完成一次旋转,线性时间函数,无限循环 */
}

js

Page({
  data: {
    icon1: '../img/process_1.png',
    icon2: '../img/process_2.png',
    icon3: '../img/process_3.png',
    processData: [],//节点信息
  },
  onLoad: function () {
    // 初始化数据 processData
    const data = [{
        name: '节点1',
        start: '#fff',
        end: '#f9d9dd',
        icon: this.data.icon1,
        state: 0
      },
      {
        name: '节点2',
        start: '#f9d9dd',
        end: '#f9d9dd',
        icon: this.data.icon1,
        state: 0
      },
      {
        name: '节点3',
        start: '#f9d9dd',
        end: '#f9d9dd',
        icon: this.data.icon1,
        state: 1
      },
      {
        name: '节点4',
        start: '#f9d9dd',
        end: '#f9d9dd',
        icon: this.data.icon1,
        state: 0
      },
      {
        name: '节点5',
        start: '#f9d9dd',
        end: '#fff',
        icon: this.data.icon1,
        state: 0
      }
    ]
    this.setData({
      processData: data
    })
    // 处理节点信息
    this.setProcessIcon();
  },
  setProcessIcon: function () {
    const processArr = this.data.processData;
    let index = -1; // 记录状态为1的最后的位置
  
    // 首先找到状态为1的节点位置
    processArr.forEach((item, i) => {
      if (item.state === 1) {
        index = i;
        return false; // 找到后停止循环
      }
    });
  
    // 然后根据找到的位置设置图标和其他属性
    processArr.forEach((item, i) => {
      if (i === index) { // 当前正在处理的节点
        item.icon = this.data.icon2;
        item.start = "#f0a0a9";
        item.end = "#f9d9dd";
      } else if (i < index && index !== -1) { // 已完成的节点
        item.icon = this.data.icon3;
        item.start = "#f0a0a9";
        item.end = "#f0a0a9";
      } else { // 未完成的节点
        item.icon = this.data.icon1;
        item.start = "#f9d9dd";
        item.end = "#f9d9dd";
      }
    });
  
    // 特殊处理第一个和最后一个节点
    if (processArr.length > 0) {
      processArr[0].start = "#fff";
      processArr[processArr.length - 1].end = "#fff";
    }
  
    this.setData({
      processData: processArr
    });
  }
});

纵向效果

代码

wxml

<view class='order_process1'>
  <view class='process_item1' wx:for="{{processData}}" wx:key="index">
    <view class='process_vertical1'>
      <view class='process_line_vertical1' style="background:{{item.start}};"></view>
      <image class="process_icon1 {{item.icon === icon2 ? 'rotate-icon1' : ''}}" src="{{item.icon}}"></image>
      <view class='process_line_vertical1' style="background:{{item.end}};"></view>
    </view>
    <text class='process_name1'>{{item.name}}</text>
  </view>
</view>

wxss

.order_process1 {
  display: flex;
  flex-direction: column; /* 改为垂直排列 */
  padding: 10rpx;
  background-color: #fff;
}

.process_item1 {
  display: flex;
  flex-direction: row; /* 每个步骤项水平排列 */
  align-items: center; /* 垂直居中对齐 */
}

.process_vertical1 {
  display: flex;
  flex-direction: column; /* 步骤线垂直排列 */
  align-items: center; /* 图标居中 */
}

.process_icon1 {
  width: 35rpx;
  height: 35rpx;
}

.process_line_vertical1 {
  background: #eff3f6;
  width: 5rpx; /* 宽度变为5rpx */
  height: 40rpx; /* 高度根据需要设置 */
}

.process_name1 {
  font-size: 24rpx;
  margin-left: 20rpx; /* 文字与图标之间的间距 */
}

/* 定义旋转动画 */
@keyframes rotate360 {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

/* 应用到 .rotate-icon1 类 */
.rotate-icon1 {
  animation: rotate360 5s linear infinite; /* 5秒完成一次旋转,线性时间函数,无限循环 */
}

js

参见横向js代码

图片展示 

process_1.png

process_2.png

process_3.png

扩展

已完成的状态均设置为1

Page({
  data: {
    icon1: '../img/process_1.png',
    icon2: '../img/process_2.png',
    icon3: '../img/process_3.png',
    processData: [], // 节点信息
  },
  onLoad: function () {
    // 初始化数据 processData, 将当前节点及之前的所有节点状态设置为1
    const data = [{
        name: '节点1',
        start: '#fff',
        end: '#f9d9dd',
        icon: this.data.icon1,
        state: 1 // 已完成
      },
      {
        name: '节点2',
        start: '#f9d9dd',
        end: '#f9d9dd',
        icon: this.data.icon1,
        state: 1 // 已完成
      },
      {
        name: '节点3',
        start: '#f9d9dd',
        end: '#f9d9dd',
        icon: this.data.icon1,
        state: 1 // 当前处理中
      },
      {
        name: '节点4',
        start: '#f9d9dd',
        end: '#f9d9dd',
        icon: this.data.icon1,
        state: 0 // 未开始
      },
      {
        name: '节点5',
        start: '#f9d9dd',
        end: '#fff',
        icon: this.data.icon1,
        state: 0 // 未开始
      }
    ];
    this.setData({
      processData: data
    });
    // 处理节点信息
    this.setProcessIcon();
  },
  setProcessIcon: function () {
    const processArr = this.data.processData;
    let index = -1; // 记录状态为1的最后的位置

    // 首先找到状态为1的最后节点位置
    processArr.forEach((item, i) => {
      if (item.state === 1) {
        index = i;
      }
    });

    // 然后根据找到的位置设置图标和其他属性
    processArr.forEach((item, i) => {
      if (i <= index) { // 当前正在处理的节点和已完成的节点
        item.icon = i === index ? this.data.icon2 : this.data.icon3;
        item.start = i === 0 ? "#fff" : "#f0a0a9";
        item.end = i === index ? "#f9d9dd" : "#f0a0a9";
      } else { // 未完成的节点
        item.icon = this.data.icon1;
        item.start = "#f9d9dd";
        item.end = "#f9d9dd";
      }
    });

    // 特殊处理最后一个节点
    if (processArr.length > 0) {
      processArr[processArr.length - 1].end = "#fff";
    }

    this.setData({
      processData: processArr
    });
  }
});

数据部分优化

其实可以只保留name和state

const data = [{
    name: '节点1',
    start: '',
    end: '',
    icon: '',
    state: 1 
  },
  {
    name: '节点2',
    start: '',
    end: '',
    icon: '',
    state: 1 
  },
  {
    name: '节点3',
    start: '',
    end: '',
    icon: '',
    state: 1 
  },
  {
    name: '节点4',
    start: '',
    end: '',
    icon: '',
    state: 0 
  },
  {
    name: '节点5',
    start: '',
    end: '',
    icon: '',
    state: 0 
  }
];

新的逻辑效果:调整正在直接节点的位置(附加完整解析)

将正在执行的节点,设置为最后一个已完成节点的下一个节点

Page({
  data: {
    icon1: '../img/process_1.png',//未完成图标
    icon2: '../img/process_2.png',//正在进行的图标
    icon3: '../img/process_3.png',//已完成图标
    processData: [], // 节点信息
  },
  onLoad: function () {
    // 初始化数据 ,name:显示的节点名称;start:表示开始线段的颜色;end:表示结束线段的颜色;icon:表示节点的图标显示
    // state:表示当前节点的状态(已完成表示1,未完成表示0)
    // 这里的start和end表示节点的前后两条线段,每个节点都含有start前线段和end尾线段
    const data = [
      {name: '节点1',start: '',end: '',icon: '',state: 1},
      {name: '节点2',start: '',end: '',icon: '',state: 1},
      {name: '节点3',start: '',end: '',icon:'',state: 0},
      {name: '节点4',start: '',end: '',icon:'',state: 0},
      {name: '节点5',start: '',end: '',icon:'',state: 0}
    ];
    this.setData({
      processData: data
    });
    // 处理节点信息
    this.setProcessIcon();
  },
  //将节点信息进行效果展示
  setProcessIcon: function () {
    const processArr = this.data.processData;//获取节点的完整信息
    let lastIndex = -1; // 记录状态为1的最后的位置
    // 找到状态为1的最后节点位置
    processArr.forEach((item, i) => {
      // 循环全部节点信息,如果state为1,就设置lastIndex的值为当前索引,
      // 这里一直会被赋值知道最后一个sate为1出现,lastIndex的值就为最后一个satae的索引,
      // 例如这里第一条,和第二条state为1,第一次对第一条进行赋值,lastIndex = 0;第二次对第二条进行赋值,lastIndex = 1,所以lastIndex = 1
      if (item.state === 1) {
        lastIndex = i;
      }
    });
    // 判断是否所有节点都已完成或未开始:allCompleted和allNotStarted的数值是bool类型,如果后面判断成立为true,反之为false
    // 判断是否全部完成:判断最后一个节点的索引是否是节点信息长度-1。(判断最后一个为状态为1的节点是否是最后一个节点)
    // 例如有三条数据,我的索引是0,1,2;如果我索引是2,那要对齐需要长度3-1才行
    const allCompleted = lastIndex === processArr.length - 1;
    // 判断是否全部未完成:如果lastIndex在<找到状态为1的最后节点位置>中为进行赋值,也就是为初始数据-1的时候,表明数据中没有state为1的数据,那就是全都未开始
    const allNotStarted = lastIndex === -1;
    // 根据条件设置图标和其他属性,循环全部节点信息对每一项进行设置
    processArr.forEach((item, i) => {
      // 如果所有节点都未开始
      if (allNotStarted) { 
        // 如果当前索引等于0(也就是第一个节点(索引为0)):就设置图标为正在执行状态icon2。
        // 反之(其余节点)设置图标为未完成图标icon1
        item.icon = i === 0 ? this.data.icon2 : this.data.icon1;
        // 如果当前索引等于0(也就是第一个节点(索引为0)):就设置节点的开始线段为白色(和背景色一致表示隐藏)。
        // 反之(其余节点)设置开始线段的颜色为未完成的浅粉色
        // 简而言之,设置第一个节点的开始线段隐藏,其余节点的开始线段为浅粉色
        item.start = i === 0 ? "#fff" : "#f9d9dd";
        // 如果当前索引等于全部节点长度-1(也就是最后节点):就设置节点的结束线段为白色(和背景色一致表示隐藏)。
        // 反之(其余节点)设置结束线段的颜色为未完成的浅粉色
        // 简而言之,设置最后节点的结束线段隐藏,其余节点的结束线段为浅粉色
        item.end = i === processArr.length - 1 ? "#fff" : "#f9d9dd";
      }
      // 如果所有节点都已完成
      else if (allCompleted) { 
        //设置全部节点的图标为已完成图标icon3
        item.icon = this.data.icon3;
        //如果是第一个节点,那么它的开始线段隐藏(白色);如果是其余节点,开始线段设置为深粉色
        item.start = i === 0 ? "#fff" : "#f0a0a9";
        //如果是最后一个节点,那么它的结束线段隐藏(白色);如果是其余节点,结束线段设置为深粉色
        item.end = i === processArr.length - 1 ? "#fff" : "#f0a0a9";
      }
      // 中间节点-正常情况
      else { 
        // 已完成的节点
        // 如果当前索引小于等于最后一个state为1的节点,表示已完成(这里的例子lastIndex=1,也就是这里前两条0,1满足)
        if (i <= lastIndex) { 
          //设置已完成的节点图标为icon3
          item.icon = this.data.icon3;
          //设置第一个节点的开始线段隐藏(白色),反之已完成的节点的开始线段为深粉色
          item.start = i === 0 ? "#fff" : "#f0a0a9";
          //设置最后一个节点的结束线段隐藏(白色),反之已完成的节点的结束线段为深粉色
          item.end = i === lastIndex ? "#f0a0a9" : "#f0a0a9";
        } 
        // 下一个即将开始的节点
        // 这里设置正在执行的节点(节点未完成,但正在执行,介于未完成和已完成之间)
        // 该节点位于state=1的数据的下一条数据,也就是最后一个于state=1的索引+1
        else if (i === lastIndex + 1) {
          //设置这个正在进行的节点的图标为icon2
          item.icon = this.data.icon2;
          //设置这个节点的开始线段为深粉色(已完成线段颜色)
          item.start = "#f0a0a9";
          //设置这个节点的结束线段为浅粉色(未完成线段颜色)
          item.end = "#f9d9dd";
        } 
        // 未完成的节点
        else { 
          //设置节点图标为icon
          item.icon = this.data.icon1;
          //设置全部未完成节点的开始线段为浅粉色
          item.start = "#f9d9dd";
          //如果是最后一个节点,就隐藏它的结束线段(白色),如果不是最后一个节点,就设置结束线段颜色为浅粉色
          item.end = i === processArr.length - 1 ? "#fff" : "#f9d9dd";
        }
      }
    });
    //替换新的节点数据
    this.setData({
      processData: processArr
    });
  }
});

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

25号底片~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值