vue3水波柱状图 ,实现

 效果图

//引用页面
<div style="height: 60px;background-color: #fff;border-radius: 5px;width: 40px;">

    <WavePercentage

     :percentage="progress"

     primary-color="#ffcb7c"

     secondary-color="#ffcb7c"

    />

</div>

import WavePercentage from './WavePercentage.vue'
const progress = ref(60)

//WavePercentage页面
<template>
    <div ref="containerRef" class="wave-percentage-container">
      <canvas ref="canvasRef" class="wave-canvas"></canvas>
    </div>
  </template>
  
  <script setup>
  import { ref, onMounted, onUnmounted, defineProps, withDefaults } from 'vue'
  
  class Wave {
    constructor(height, power = 60) {
      this.__force = 0
      this.__wavePower = power
      this.__count = 0
      this.__y = height
      this.__h = height
      this.__pos = 0
    }
  
    get height() {
      return this.__h
    }
  
    set height(value) {
      this.__h = Math.max(0, value)
    }
  
    get percent() {
      return (1 - this.__y / this.__h) * 100
    }
  
    set percent(value) {
      this.__y = this.__h * (1 - value / 100)
    }
  
    update() {
      // 添加一个持续的波动效果,但不改变实际百分比
      this.__count += 0.1
      this.__pos += 0.04
      this.__force = Math.sin(this.__count)
    }
  
    draw(ctx, w, h, makeGrd, percentage) {
      // 固定波浪高度,但添加动态波动效果
      const actualY = this.__h * (1 - percentage / 100)
      
      ctx.fillStyle = makeGrd(h)
      ctx.beginPath()
      ctx.moveTo(0, actualY)
  
      const p = Math.sin(this.__pos)
      ctx.quadraticCurveTo(
        w * (p + 0.25), 
        actualY + (this.__wavePower * this.__force * 0.2), 
        w * (p + 0.5), 
        actualY
      )
      ctx.quadraticCurveTo(
        w * (p + 0.75), 
        actualY - (this.__wavePower * this.__force * 0.2), 
        w, 
        actualY
      )
      ctx.lineTo(w, h)
      ctx.lineTo(0, h)
      ctx.lineTo(0, actualY)
      ctx.closePath()
      ctx.fill()
    }
  }
  
  // Props definition
  const props = defineProps({
    percentage: {
      type: Number,
      default: 50,
      validator: (value) => value >= 0 && value <= 100
    },
    wavePower: {
      type: Number,
      default: 100
    },
    primaryColor: {
      type: String,
      default: '#419dff'
    },
    secondaryColor: {
      type: String,
      default: '#66bfff'
    }
  })
  
  // Refs for canvas and container
  const containerRef = ref(null)
  const canvasRef = ref(null)
  
  // Animation variables
  let animationFrameId = null
  let wave = null
  let ctx = null
  
  // Create gradient function
  const makeGrd = (ctx, h, primaryColor, secondaryColor) => {
    const g = ctx.createLinearGradient(0, 0, 0, h)
    g.addColorStop(0, primaryColor)
    g.addColorStop(1, secondaryColor)
    return g
  }
  
  // Draw function
  const draw = () => {
    if (!containerRef.value || !canvasRef.value) return
  
    const canvas = canvasRef.value
    const container = containerRef.value
  
    // Update dimensions
    const w = canvas.width = container.clientWidth
    const h = canvas.height = container.clientHeight
  
    // Recreate wave if not exists or dimensions changed
    if (!wave || wave.height !== h) {
      wave = new Wave(h, props.wavePower)
    }
  
    // Update wave animation
    wave.update()
  
    ctx = canvas.getContext('2d')
  
    // Clear canvas
    ctx.globalCompositeOperation = 'source-over'
    ctx.fillStyle = "rgba(255, 255, 255, 1)"
    ctx.fillRect(0, 0, w, h)
  
    ctx.globalCompositeOperation = 'xor'
    ctx.fillStyle = "rgba(0, 0, 0, 1)"
    ctx.fillRect(0, 0, w, h)
  
    // Draw wave with current percentage
    wave.draw(ctx, w, h, (h) => makeGrd(ctx, h, props.primaryColor, props.secondaryColor), props.percentage)
  
    // Draw percentage text
    ctx.textAlign = "center"
    ctx.textBaseline = 'middle'
    ctx.font = 'bold 1rem serif'
    ctx.fillStyle = "rgba(0, 0, 0, 1)"
    ctx.fillText(`${props.percentage | 0}%`, w / 2, h / 2)
  
    // Continue animation
    animationFrameId = requestAnimationFrame(draw)
  }
  
  // Lifecycle hooks
  onMounted(() => {
    animationFrameId = requestAnimationFrame(draw)
  })
  
  onUnmounted(() => {
    if (animationFrameId) {
      cancelAnimationFrame(animationFrameId)
    }
  })
  </script>
  
  <style scoped>
  .wave-percentage-container {
    position: relative;
    width: 100%;
    height: 100%;
  }
  
  .wave-canvas {
    width: 100%;
    height: 100%;
  }
  </style>

直接贴代码可以直接使用,支持动态设置值,修改波纹颜色

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值