You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Angular/Tailwind点状进度条添加滑动着色动画

Angular + Tailwind 圆点进度条滑动动画实现方案

需求回顾

基于Angular和Tailwind开发圆点式进度/音量条:

  • 圆点总数对应百分比(如20个点=0-100%,每步5%),全灰为0%,全蓝为100%
  • 点击进度条时,着色部分需平滑滑动到点击位置
  • 曾尝试clip-path实现,未达成预期效果

实现方案:双层圆点叠加+滑动遮罩

放弃clip-path的复杂路径计算,改用底层彩色圆点+上层灰色滑动遮罩的方案,通过CSS过渡实现平滑动画:

步骤1:组件TS代码修改

新增动画相关计算逻辑,监听value变化并计算滑动偏移量:

import { Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges } from '@angular/core';

export class ProgressBarComponent implements OnInit, OnChanges {
  @Input() min = 0;
  @Input() max = 100;
  @Input() value = 0;
  @Input() step = 5;
  @Input() dotSize: 'xs' | 'sm' | 'md' | 'lg' | 'xl' = 'lg';
  @Input() dotSpacing = 0; // Gap between dots in pixels

  @Output() valueChange = new EventEmitter<number>();

  public dots = Array.from(
    { length: Math.floor(this.max / this.step) },
    (_, i) => i * this.step + this.step,
  );
  public dotPixels = 0;
  public dotTotalWidth = 0; // 单个圆点总宽度(尺寸+间距)
  public animatedOffset = 0; // 滑动遮罩的偏移量

  ngOnInit(): void {
    this.calculateDotSize();
    this.calculateOffset();
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes['value']) {
      this.calculateOffset();
    }
    if (changes['dotSize'] || changes['dotSpacing']) {
      this.calculateDotSize();
      this.calculateOffset();
    }
  }

  private calculateDotSize(): void {
    switch (this.dotSize) {
      case 'xs':
        this.dotPixels = 6;
        break;
      case 'sm':
        this.dotPixels = 8;
        break;
      case 'md':
        this.dotPixels = 10;
        break;
      case 'lg':
        this.dotPixels = 12;
        break;
      case 'xl':
        this.dotPixels = 16;
        break;
    }
    this.dotTotalWidth = this.dotPixels + this.dotSpacing;
  }

  private calculateOffset(): void {
    // 计算已激活的圆点数量
    const activeDotCount = Math.floor(this.value / this.step);
    // 计算遮罩需要滑动的偏移量:总宽度 - 已激活圆点的总宽度
    this.animatedOffset = (this.dots.length - activeDotCount) * this.dotTotalWidth;
  }

  public onDotClick(value: number) {
    this.valueChange.emit(value);
  }
}

步骤2:模板HTML修改

重构结构为双层圆点+滑动遮罩,添加CSS过渡动画:

<div class="progress-bar-wrapper relative">
  <!-- 底层:全彩色圆点 -->
  <div class="flex items-center">
    <ng-container *ngFor="let dotVal of dots; let i = index">
      <div
        class="dot py-1 hover:cursor-pointer"
        [style.padding-left]="dotSpacing / 2 + 'px'"
        [style.padding-right]="dotSpacing / 2 + 'px'"
        (click)="onDotClick(dotVal)"
      >
        <svg
          class="fill-accent"
          [attr.width]="dotPixels + 'px'"
          [attr.height]="dotPixels + 'px'"
        >
          <circle
            [attr.cx]="dotPixels / 2"
            [attr.cy]="dotPixels / 2"
            [attr.r]="dotPixels / 2"
            stroke-width="1"
          />
        </svg>
      </div>
    </ng-container>
  </div>

  <!-- 上层:灰色滑动遮罩(带overflow:hidden) -->
  <div 
    class="absolute top-0 left-0 flex items-center transition-transform duration-300 ease-in-out"
    [style.transform]="'translateX(' + animatedOffset + 'px)'"
    [style.width]="'calc(100% - ' + animatedOffset + 'px)'"
    style="overflow: hidden;"
  >
    <ng-container *ngFor="let dotVal of dots; let i = index">
      <div
        class="dot py-1 hover:cursor-pointer"
        [style.padding-left]="dotSpacing / 2 + 'px'"
        [style.padding-right]="dotSpacing / 2 + 'px'"
        (click)="onDotClick(dotVal)"
      >
        <svg
          class="fill-zinc-600"
          [attr.width]="dotPixels + 'px'"
          [attr.height]="dotPixels + 'px'"
        >
          <circle
            [attr.cx]="dotPixels / 2"
            [attr.cy]="dotPixels / 2"
            [attr.r]="dotPixels / 2"
            stroke-width="1"
          />
        </svg>
      </div>
    </ng-container>
  </div>
</div>

关键实现说明

  1. 双层结构逻辑:
    • 底层固定显示全彩色圆点,作为进度条的着色部分
    • 上层灰色圆点容器通过transform: translateX()滑动,遮住底层未激活的彩色圆点,形成"已激活区域"的视觉效果
  2. 偏移量计算:
    • 根据当前value算出已激活的圆点数量,进而计算遮罩需要滑动的距离,确保滑动后刚好露出对应数量的彩色圆点
  3. 平滑动画:
    • 给上层容器添加Tailwind的transition-transform duration-300 ease-in-out类,实现0.3秒的平滑过渡效果

效果展示

Progress bar sample

内容的提问来源于stack exchange,提问作者Michael Stowe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 16:04:58