canvas 模仿抽奖转盘

摸鱼摸鱼,闲来无事就是摸鱼

搞个营销类的转盘抽奖活动

转盘抽奖

先看一下效果图

思路

做完项目Google一下发现还可以通过css 的方式去做,这边主要通过canvas的方法去绘制

先生成一个基础的canvas

<canvas id="canvas" width="600" height="600"></canvas>

首先绘制一个圆的背景(也可以通过图片来设置背景)

js 复制代码
const drawBall = (beginPointX,beginPointY,radius,startAngle,endAngle,backgroudColor) =>{
  const ctx = canvasOptions.ctx
  ctx.beginPath();
  ctx.arc(beginPointX,beginPointY,radius,startAngle,endAngle);
  ctx.fillStyle = backgroudColor;
  ctx.fill();
}
// 设置背景为图片 获取最小值 通过半径来判断 缩放值
const drawRoundImage = (beginPointX,beginPointY,radius,startAngle,endAngle,backImage) =>{
  const ctx = canvasOptions.ctx
  let temp = Math.min(backImage.width,backImage.height);
  let scale = radius / temp;
  ctx.rect(beginPointX-backImage.width*scale, beginPointX-backImage.height*scale, beginPointX+radius, beginPointY+backImage.width*scale.height*scale);
  ctx.drawImage(backImage, beginPointX-backImage.width*scale/2, beginPointY-backImage.height*scale/2,backImage.width*scale,backImage.height*scale);
}

画完背景就里面抽奖的内容了

js 复制代码
// 根据抽奖的内容列表来判断每次分配所占的角度
for (const [index,item] of ballOptions.entries()){
    let startAngle = 0;
    let endAngle = 0;
    if(index){
      startAngle = 2*Math.PI*(index/ballOptions.length);
    }
    if(index !=ballOptions.length-1){
      endAngle = 2*Math.PI*((index+1)/ballOptions.length);
    }
    await drawArc(300,300,200,startAngle+angle.value,endAngle+angle.value,item.color,item)
  }
  // 转盘里面的内容 包括图片 背景颜色 和 字体内容
const drawArc=(beginPointX,beginPointY,radius,startAngle,endAngle,backgroudColor,item)=>{
  const centerX = 300;
  const centerY = 300;
  const orbitRadius = 200
  const ctx = canvasOptions.ctx
  ctx.beginPath();
  ctx.arc(beginPointX,beginPointY,radius,startAngle,endAngle);
  if(backgroudColor){
    ctx.fillStyle = backgroudColor;
    ctx.strokeStyle ="white";
  }else{
    ctx.fillStyle = "rgba(0,0,0,0)";
  }
  ctx.fill();
  ctx.beginPath()
  ctx.moveTo(300,300)
  const startX = centerX + orbitRadius * Math.cos(startAngle);  
  const startY = centerY + orbitRadius * Math.sin(startAngle);
  const endX = centerX + orbitRadius * Math.cos(endAngle);  
  const endY = centerY + orbitRadius * Math.sin(endAngle);
  ctx.lineTo(startX,startY)
  ctx.lineTo(endX,endY);
  ctx.closePath();
  ctx.fillStyle = backgroudColor;
  ctx.strokeStyle = backgroudColor
  ctx.stroke();
  ctx.fill();
  // 保存字体样式
  ctx.save();
  ctx.translate((startX+endX)/2, (startY+endY)/2);
  // 根据圆的弧度计算文字旋转角度
  let angle = 0
  let index = ballOptions.findIndex(ite =>ite.label == item.label);
  if(index != ballOptions.length-1){
    angle = (startAngle + endAngle) / 2 + Math.PI/2;
  }else{
    angle = (startAngle + endAngle) / 2 - Math.PI/2;
  }
  // 旋转画布
  ctx.rotate(angle);
  ctx.font = '24px Arial bold';
  ctx.fillStyle = '#AA625B';
  ctx.textAlign = "center"
  ctx.fillText(item.label, 0,0);
  ctx.restore();
  if("backImage" in item){
    let img = new Image();
    img.src = item.backImage;
    img.onload=()=>{
      ctx.save();
      ctx.translate(((startX+endX)/2+300)/2, ((startY+endY)/2+300)/2);
      let angle = 0
      let index = ballOptions.findIndex(ite =>ite.label == item.label);
      if(index != ballOptions.length-1){
        angle = (startAngle + endAngle) / 2 + Math.PI/2;
      }else{
        angle = (startAngle + endAngle) / 2 - Math.PI/2;
      }
      // 旋转画布
      ctx.rotate(angle);
      drawRoundImage(0,0,60,0,Math.PI*2,img)
      ctx.restore();
    }
  }
}

最后就是转盘的动画内容了 包括旋转角速度的变化 以及一些后门的逻辑

js 复制代码
 if(isAnimal.value){
    speed.value += 0.0005;
    angle.value += speed.value;
    // console.log(angle.value);
    animateFlag.value = requestAnimationFrame(animate);
  }
  if(speed.value > 0.32){
    stopAnimate();
  }
  
const stopAnimate = () =>{
  // 计算减缓速度 设置减缓的时间
  // 设置一个延时暂停然后
  // 后门计算说谁谁必中
  if(mustIndex.value){
    if(!startAngle.value){
      mustIndex.value = ballOptions.length-mustIndex.value-2
    }
    startAngle.value = ((mustIndex.value)*Math.PI*2)/ballOptions.length; // 指针应该停的位置
    endAngle.value = ((mustIndex.value+1)*Math.PI*2)/ballOptions.length; // 指针应该挺的位置
    let startAngle1 = startAngle.value;
    let endAngle1  = endAngle.value
    let stopAngle = (startAngle1+endAngle1)/2; // 暂停的中间值
    let baseAngle = angle.value % (2*Math.PI) // 当前的旋转的所在的位置
    // console.log(stopAngle);
    downSpeed.value = stopAngle<baseAngle?(2*Math.PI-baseAngle+stopAngle)/1000:(stopAngle-baseAngle)/1000;
    subSpeed();
  }else{
    // console.log(startAngle.value);
    // console.log("6");
    subSpeed();
  }
}

const subSpeed=()=>{
  speed.value -= downSpeed.value;
  if(mustIndex.value){
    if(!(angle.value % (2*Math.PI)>startAngle.value
        &&
      angle.value % (2*Math.PI)<endAngle.value)){
      speedFlag.value = requestAnimationFrame(subSpeed);
    }else if(speed.value<0){
      // console.log("6");
      cancelAnimationFrame(animateFlag.value)
      cancelAnimationFrame(speedFlag.value)
    }
    else{
      // angle.value = startAngle.value;
      cancelAnimationFrame(animateFlag.value)
      cancelAnimationFrame(speedFlag.value)
    }
  }else{
    if(speed.value<0){
      cancelAnimationFrame(animateFlag.value)
      cancelAnimationFrame(speedFlag.value)
      // console.log("6");
    }else{
      speedFlag.value = requestAnimationFrame(subSpeed);
    }
  }
}

const startAnimate = ()=>{
  isAnimal.value = true
  animate();
  speed.value = 0.1
}

具体代码在 github.com/chenchuchun... 感觉是想复杂也写复杂了 damn

相关推荐
一只不会编程的猫1 分钟前
高德地图自定义折线矢量图形
前端·vue.js·vue
m0_748250933 分钟前
html 通用错误页面
前端·html
来吧~12 分钟前
vue3使用video-player实现视频播放(可拖动视频窗口、调整大小)
前端·vue.js·音视频
鎈卟誃筅甡25 分钟前
Vuex 的使用和原理详解
前端·javascript
呆呆小雅30 分钟前
二、创建第一个VUE项目
前端·javascript·vue.js
m0_7482393336 分钟前
前端(Ajax)
前端·javascript·ajax
Fighting_p40 分钟前
【记录】列表自动滚动轮播功能实现
前端·javascript·vue.js
前端Hardy42 分钟前
HTML&CSS:超炫丝滑的卡片水波纹效果
前端·javascript·css·3d·html
技术思考者1 小时前
HTML速查
前端·css·html
缺少动力的火车1 小时前
Java前端基础—HTML
java·前端·html