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

相关推荐
码事漫谈14 分钟前
解决 Anki 启动器下载错误的完整指南
前端
im_AMBER34 分钟前
Web 开发 27
前端·javascript·笔记·后端·学习·web
蓝胖子的多啦A梦1 小时前
低版本Chrome导致弹框无法滚动的解决方案
前端·css·html·chrome浏览器·版本不同造成问题·弹框页面无法滚动
玩代码1 小时前
vue项目安装chromedriver超时解决办法
前端·javascript·vue.js
訾博ZiBo1 小时前
React 状态管理中的循环更新陷阱与解决方案
前端
StarPrayers.2 小时前
旅行商问题(TSP)(2)(heuristics.py)(TSP 的两种贪心启发式算法实现)
前端·人工智能·python·算法·pycharm·启发式算法
一壶浊酒..2 小时前
ajax局部更新
前端·ajax·okhttp
DoraBigHead3 小时前
React 架构重生记:从递归地狱到时间切片
前端·javascript·react.js
彩旗工作室3 小时前
WordPress 本地开发环境完全指南:从零开始理解 Local by Flywhee
前端·wordpress·网站
iuuia4 小时前
02--CSS基础
前端·css