<template>
  <view class="canvas-prize-wheel">
    <!-- 绿色背景 -->
    <view class="prize-strip-background"></view>
    
    <canvas 
      id="prizeCanvas" 
      ref="prizeCanvas"
      canvas-id="prizeCanvas"
      :style="canvasStyle"
      @touchstart="onTouchStart"
      @touchmove="onTouchMove"
      @touchend="onTouchEnd">
    </canvas>
    
    <!-- 中心指示器 -->
    <view class="center-indicator"></view>
  </view>
</template>

<script>
export default {
  name: 'CanvasPrizeWheel',
  props: {
    // 奖品列表
    prizes: {
      type: Array,
      default: () => []
    },
    // 动画时长(秒)
    duration: {
      type: Number,
      default: 4
    },
    // 单个奖品宽度
    itemWidth: {
      type: Number,
      default: () => uni.upx2px(180)
    },
    // 单个奖品高度
    itemHeight: {
      type: Number,
      default: () => uni.upx2px(150)
    },
    // 最大速度
    maxSpeed: {
      type: Number,
      default: 2000
    },
    // 是否允许手动滑动
    allowDrag: {
      type: Boolean,
      default: false
    },
    // 背景色
    backgroundColor: {
      type: String,
      default: 'rgba(255, 255, 255, 0.8)'
    },
    // 高亮背景色
    highlightColor: {
      type: String,
      default: 'rgba(255, 215, 0, 0.3)'
    },
    // 高亮阴影颜色
    highlightShadow: {
      type: String,
      default: 'rgba(255, 215, 0, 0.5)'
    },
    // 边框圆角
    borderRadius: {
      type: Number,
      default: 8
    }
  },
  data() {
    return {
      canvas: null,           // Canvas对象
      ctx: null,              // 绘图上下文
      canvasWidth: 0,         // Canvas宽度
      canvasHeight: 0,        // Canvas高度
      isSpinning: false,      // 是否正在旋转
      activeIndex: -1,        // 当前高亮索引
      currentOffset: 0,       // 当前偏移量
      targetPrize: null,      // 目标奖品
      targetIndex: -1,        // 目标奖品索引
      speedPhase: 'initial',  // 速度阶段
      animationId: null,      // 动画ID
      lastTimestamp: 0,       // 上次时间戳
      speed: 0,               // 当前速度(px/s)
      dragInfo: {             // 拖动信息
        isDragging: false,
        startX: 0,
        lastX: 0,
        startOffset: 0
      },
      pixelRatio: 1,          // 设备像素比
      itemsCount: 0,          // 实际奖品数量
      repeatCount: 1,         // 奖品重复次数
      isInitialized: false,   // 是否已初始化
    }
  },
  computed: {
    canvasStyle() {
      return {
        width: '100%',
        height: `${this.itemHeight}px`,
        zIndex: 2
      }
    },
    
    // 计算中心位置偏移量
    centerOffset() {
      return this.canvasWidth / 2 - this.itemWidth / 2
    },
    
    // 单个周期的宽度(一组奖品的总宽度)
    cycleWidth() {
      return this.itemWidth * this.prizes.length
    }
  },
  mounted() {
    // 延迟初始化以确保DOM已渲染
    this.$nextTick(() => {
      setTimeout(() => {
        this.initCanvas()
        this.itemsCount = this.prizes.length
      }, 100)
    })
    
    // 监听屏幕旋转或大小变化
    window.addEventListener('resize', this.onResize)
  },
  beforeDestroy() {
    // 清理资源
    this.stopAnimation()
    window.removeEventListener('resize', this.onResize)
  },
  methods: {
    // 初始化Canvas
    async initCanvas() {
      try {
        // 获取canvas元素
        const query = uni.createSelectorQuery().in(this)
        await new Promise(resolve => {
          query.select('#prizeCanvas')
            .fields({ node: true, size: true, rect: true, context: true })
            .exec(res => {
              console.log('Canvas查询结果:', res)
              
              if (!res || !res[0]) {
                console.warn('无法获取Canvas节点，使用降级方案')
                this.initCanvasContext()
                resolve()
                return
              }
              
              // 原生Canvas2D
              if (res[0].node) {
                const canvas = res[0].node
                const ctx = canvas.getContext('2d')
                
                // 获取设备像素比
                this.pixelRatio = uni.getSystemInfoSync().pixelRatio || 1
                console.log('设备像素比:', this.pixelRatio)
                
                // 设置canvas的尺寸
                this.canvasWidth = res[0].width || uni.getSystemInfoSync().windowWidth
                this.canvasHeight = this.itemHeight
                
                console.log('Canvas尺寸:', this.canvasWidth, this.canvasHeight)
                
                // 设置Canvas尺寸
                canvas.width = this.canvasWidth * this.pixelRatio
                canvas.height = this.canvasHeight * this.pixelRatio
                
                // 根据像素比例缩放上下文
                ctx.scale(this.pixelRatio, this.pixelRatio)
                
                this.canvas = canvas
                this.ctx = ctx
              } 
              // 使用普通Canvas API
              else if (res[0].context) {
                this.ctx = res[0].context
                this.canvasWidth = res[0].width || uni.getSystemInfoSync().windowWidth
                this.canvasHeight = this.itemHeight
              }
              
              // 设置初始位置
              this.resetPosition()
              
              // 开始渲染
              this.isInitialized = true
              this.render()
              
              resolve()
            })
        })
      } catch (error) {
        console.error('初始化Canvas失败:', error)
        this.initCanvasContext() // 降级方案
      }
    },
    
    // 降级方案：使用2D上下文
    initCanvasContext() {
      console.log('使用降级Canvas API')
      const sysInfo = uni.getSystemInfoSync()
      this.canvasWidth = sysInfo.windowWidth
      this.canvasHeight = this.itemHeight
      this.pixelRatio = sysInfo.pixelRatio || 1
      
      // 获取上下文
      this.ctx = uni.createCanvasContext('prizeCanvas', this)
      
      // 设置初始位置
      this.resetPosition()
      
      // 开始渲染
      this.isInitialized = true
      this.render()
    },
    
    // 处理屏幕尺寸变化
    onResize() {
      // 重新初始化Canvas
      this.initCanvas()
    },
    
    // 重置位置到中心
    resetPosition() {
      // 计算初始偏移量，确保一个完整的周期显示在中间
      this.currentOffset = this.centerOffset - this.cycleWidth
    },
    
    // 开始抽奖
    startSpin() {
      if (this.isSpinning) return
      
      this.isSpinning = true
      this.speedPhase = 'accelerating'
      this.speed = 30 // 初始速度
      this.targetPrize = null
      this.targetIndex = -1
      this.lastTimestamp = performance.now()
      
      // 发出开始事件
      this.$emit('spin-start')
      
      // 启动动画循环
      this.animationId = requestAnimationFrame(this.animate)
    },
    
    // 设置最终奖品并开始减速
    setPrize(prize) {
      if (!this.isSpinning) return
      
      // 保存目标奖品
      this.targetPrize = prize
      
      // 查找目标奖品在列表中的位置
      this.targetIndex = this.prizes.findIndex(item => 
        (item.id && item.id === prize.id) || 
        (item.value && item.value === prize.value)
      )
      
      if (this.targetIndex === -1) {
        console.warn('目标奖品不在奖品列表中')
        // 如果找不到，默认使用第一个
        this.targetIndex = 0
      }
      
      // 计划减速
      setTimeout(() => {
        if (this.isSpinning) {
          this.speedPhase = 'decelerating'
        }
      }, 1000) // 匀速运行一段时间后开始减速
    },
    
    // 动画函数
    animate(timestamp) {
      if (!this.isSpinning) return
      
      // 计算时间差
      const delta = timestamp - this.lastTimestamp
      this.lastTimestamp = timestamp
      
      // 根据阶段更新速度
      switch(this.speedPhase) {
        case 'accelerating':
          // 加速阶段
          this.speed = Math.min(this.maxSpeed, this.speed * 1.08)
          if (this.speed >= this.maxSpeed) {
            this.speedPhase = 'constant'
          }
          break
          
        case 'decelerating':
          // 减速阶段
          this.speed *= 0.97
          
          // 当速度足够慢且有目标奖品时，准备停止
          if (this.speed < 200 && this.targetIndex !== -1) {
            this.speedPhase = 'stopping'
            this.prepareToStop()
          }
          break
          
        case 'stopping':
          // 停止阶段 - 速度进一步降低
          this.speed *= 0.85
          
          // 当速度非常慢时完全停止
          if (this.speed < 10) {
            this.finalizePosition()
            this.stopAnimation()
            return
          }
          break
      }
      
      // 更新位置
      this.moveStrip(delta)
      
      // 渲染当前帧
      this.render()
      
      // 继续动画
      this.animationId = requestAnimationFrame(this.animate)
    },
    
    // 移动奖品条
    moveStrip(deltaTime) {
      // 计算移动距离
      const distance = (this.speed * deltaTime) / 1000
      
      // 更新位置
      this.currentOffset -= distance
      
      // 检查是否需要重置位置（循环效果）
      this.checkResetPosition()
      
      // 计算当前中心位置的项
      this.updateActiveItem()
    },
    
    // 检查并重置位置以实现无限循环
    checkResetPosition() {
      // 如果滚动过远，重置到等效位置
      if (Math.abs(this.currentOffset) > this.cycleWidth * 3) {
        // 计算偏移量对一个周期的余数
        const remainder = this.currentOffset % this.cycleWidth
        // 重置位置，保持视觉上的连续性
        this.currentOffset = this.centerOffset - this.cycleWidth + remainder
      }
    },
    
    // 更新活跃项
    updateActiveItem() {
      // 计算当前中心位置对应的项
      const relativePos = -this.currentOffset + this.centerOffset
      // 计算相对于一个周期的位置
      const cyclePos = (relativePos % this.cycleWidth + this.cycleWidth) % this.cycleWidth
      // 计算对应项的索引
      const itemIndex = Math.floor(cyclePos / this.itemWidth)
      
      if (itemIndex >= 0 && itemIndex < this.prizes.length) {
        this.activeIndex = itemIndex
      } else {
        this.activeIndex = -1
      }
    },
    
    // 准备停止动画，对准目标奖品
    prepareToStop() {
      if (this.targetIndex === -1) return
      
      // 计算当前位置
      const relativePos = -this.currentOffset + this.centerOffset
      // 当前位置在一个周期内的偏移量
      const cyclePos = (relativePos % this.cycleWidth + this.cycleWidth) % this.cycleWidth
      // 当前项的索引
      const currentItemIndex = Math.floor(cyclePos / this.itemWidth)
      
      // 减速率因子 - 影响最终停止位置的精度
      const decelerationFactor = 0.95
      
      // 计算目标索引相对于当前索引的位置
      // 确保我们总是向前滚动到目标（不会反向）
      let stepsToTarget = this.targetIndex - currentItemIndex
      if (stepsToTarget <= 0) {
        stepsToTarget += this.prizes.length
      }
      
      // 根据当前速度微调速度，确保能够准确停在目标奖品上
      if (this.speed > 100) {
        this.speed = Math.max(100, this.speed * decelerationFactor)
      }
    },
    
    // 最终化位置，确保奖品对准中心
    finalizePosition() {
      if (this.targetIndex === -1) return
      
      // 计算当前位置
      const relativePos = -this.currentOffset + this.centerOffset
      const cyclePos = (relativePos % this.cycleWidth + this.cycleWidth) % this.cycleWidth
      const currentItemIndex = Math.floor(cyclePos / this.itemWidth)
      
      // 计算最短路径到目标奖品
      let adjustedOffset = this.currentOffset
      
      // 如果目标就在附近，直接微调位置
      if (Math.abs(currentItemIndex - this.targetIndex) <= this.prizes.length / 2) {
        const delta = (currentItemIndex - this.targetIndex) * this.itemWidth
        adjustedOffset += delta
      }
      
      // 更新位置并设置活跃项
      this.currentOffset = adjustedOffset
      this.activeIndex = this.targetIndex
      
      // 渲染最终结果
      this.render()
    },
    
    // 停止动画
    stopAnimation() {
      if (!this.isSpinning && !this.animationId) return
      
      cancelAnimationFrame(this.animationId)
      this.animationId = null
      this.isSpinning = false
      
      // 发出停止事件，传递选中的奖品
      this.$emit('spin-end', this.targetPrize || this.prizes[this.activeIndex])
    },
    
    // 触摸开始
    onTouchStart(e) {
      if (!this.allowDrag || this.isSpinning) return
      
      this.dragInfo.isDragging = true
      this.dragInfo.startX = e.touches[0].clientX
      this.dragInfo.lastX = this.dragInfo.startX
      this.dragInfo.startOffset = this.currentOffset
    },
    
    // 触摸移动
    onTouchMove(e) {
      if (!this.dragInfo.isDragging) return
      
      const x = e.touches[0].clientX
      const deltaX = x - this.dragInfo.lastX
      
      // 更新位置
      this.currentOffset += deltaX
      
      // 更新最后点击位置
      this.dragInfo.lastX = x
      
      // 计算当前活跃项
      this.updateActiveItem()
      
      // 重新渲染
      this.render()
    },
    
    // 触摸结束
    onTouchEnd() {
      if (!this.dragInfo.isDragging) return
      
      this.dragInfo.isDragging = false
      
      // 慢慢停止惯性滑动
      let momentum = (this.dragInfo.lastX - this.dragInfo.startX) / 10
      
      // 模拟惯性
      const decelerate = () => {
        if (Math.abs(momentum) < 0.1) return
        
        momentum *= 0.95
        this.currentOffset += momentum
        
        // 更新活跃项
        this.updateActiveItem()
        
        // 重新渲染
        this.render()
        
        requestAnimationFrame(decelerate)
      }
      
      requestAnimationFrame(decelerate)
    },
    
    // 渲染函数
    render() {
      if (!this.ctx || !this.isInitialized) return
      
      try {
        // 清空画布
        this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
        
        // 计算可见的奖品
        this.renderVisibleItems()
        
        // 如果是非原生Canvas上下文，需要手动绘制
        if (!this.canvas) {
          this.ctx.draw()
        }
      } catch (error) {
        console.error('渲染Canvas失败:', error)
      }
    },
    
    // 渲染可见项目
    renderVisibleItems() {
      try {
        // 确定可见区域的起始和结束位置
        const startX = -this.currentOffset
        const endX = startX + this.canvasWidth
        
        // 计算可见的第一个项目和最后一个项目
        const cycleWidth = this.cycleWidth
        const firstVisibleIndex = Math.floor(startX / this.itemWidth) - 1
        const lastVisibleIndex = Math.ceil(endX / this.itemWidth) + 1
        
        // 渲染可见的项目
        for (let i = firstVisibleIndex; i <= lastVisibleIndex; i++) {
          // 计算项目的实际索引（考虑循环）
          const realIndex = ((i % this.prizes.length) + this.prizes.length) % this.prizes.length
          const item = this.prizes[realIndex]
          
          // 计算项目的x坐标
          const x = this.currentOffset + (i * this.itemWidth)
          
          // 渲染项目
          this.renderItem(item, x, realIndex === this.activeIndex)
        }
      } catch (error) {
        console.error('渲染项目失败:', error)
      }
    },
    
    // 渲染单个奖品项
    renderItem(item, x, isActive) {
      if (!item) return
      
      try {
        // 设置奖品容器样式
        this.ctx.save()
        
        // 计算奖品位置和尺寸
        const y = 0
        const width = this.itemWidth
        const height = this.itemHeight
        const padding = Math.min(width, height) * 0.05
        
        // 绘制背景
        this.ctx.fillStyle = isActive ? this.highlightColor : this.backgroundColor
        this.ctx.beginPath()
        this.roundRect(
          x + padding, 
          y + padding, 
          width - padding * 2, 
          height - padding * 2, 
          this.borderRadius
        )
        this.ctx.fill()
        
        // 如果是活跃项，添加阴影效果
        if (isActive) {
          this.ctx.shadowColor = this.highlightShadow
          this.ctx.shadowBlur = 10
        }
        
        // 绘制文本
        const text = item.value || ''
        this.ctx.fillStyle = item.color || '#000000'
        this.ctx.font = 'bold 16px Arial'
        this.ctx.textAlign = 'center'
        this.ctx.textBaseline = 'middle'
        this.ctx.fillText(text, x + width / 2, y + height / 2)
        
        this.ctx.restore()
      } catch (error) {
        console.error('渲染奖品项失败:', error)
      }
    },
    
    // 绘制圆角矩形
    roundRect(x, y, width, height, radius) {
      const r = Math.min(radius, width / 2, height / 2)
      
      this.ctx.moveTo(x + r, y)
      this.ctx.lineTo(x + width - r, y)
      this.ctx.arcTo(x + width, y, x + width, y + r, r)
      this.ctx.lineTo(x + width, y + height - r)
      this.ctx.arcTo(x + width, y + height, x + width - r, y + height, r)
      this.ctx.lineTo(x + r, y + height)
      this.ctx.arcTo(x, y + height, x, y + height - r, r)
      this.ctx.lineTo(x, y + r)
      this.ctx.arcTo(x, y, x + r, y, r)
    },
    
    // 导出当前Canvas为图片
    async exportImage() {
      if (!this.canvas) {
        // 非原生Canvas，使用uni.canvasToTempFilePath导出
        return new Promise((resolve, reject) => {
          uni.canvasToTempFilePath({
            canvasId: 'prizeCanvas',
            success: res => resolve(res.tempFilePath),
            fail: err => reject(err)
          }, this)
        })
      } else {
        // 原生Canvas，使用toDataURL导出
        return this.canvas.toDataURL('image/png')
      }
    }
  }
}
</script>

<style lang="scss">
.canvas-prize-wheel {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  
  .prize-strip-background {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: #00a86b; /* 绿色背景 */
    z-index: 1;
  }
  
  canvas {
    position: relative;
    z-index: 2;
  }
  
  .center-indicator {
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 2px;
    height: 100%;
    background-color: #ff5a5f;
    z-index: 10;
    pointer-events: none;
    
    &:before {
      content: '';
      position: absolute;
      top: 0;
      left: 50%;
      transform: translateX(-50%);
      width: 0;
      height: 0;
      border-left: 10px solid transparent;
      border-right: 10px solid transparent;
      border-top: 10px solid #ff5a5f;
    }
    
    &:after {
      content: '';
      position: absolute;
      bottom: 0;
      left: 50%;
      transform: translateX(-50%);
      width: 0;
      height: 0;
      border-left: 10px solid transparent;
      border-right: 10px solid transparent;
      border-bottom: 10px solid #ff5a5f;
    }
  }
}
</style> 