微信小程序营销之大转盘(二)

今日编程 2018-06-22 14:00:40 ⋅ 1162 阅读

前言

大转盘营销活动大家应该都不陌生那如何用小程序实现呢?
第一个版本的大转盘都是用图片做的,奖品等信息都是不无法修改的,说白了就是没啥实际用途,作者我就直接用canvas撸了一个全手工绘制的大转盘分享给大家。此处应有掌声 : )

效果图

因为gif动态效果图实在是太大了,就截取了两张图片给大家看

项目结构

运行流程

  • 获取所有奖品列表

  • 绘制指针和转盘

  • 绑定指针区域点击事件

  • 点击指针开始转动

  • 请求后端获取抽中的奖品

  • 调用大转盘停止方法把奖品传进去

  • 大转盘停止转动

  • 展示奖品和信息

关键代码

绘制大转盘和绑定指针点击事件

 // 启用事件
    point.inputEvent = true;
    point.onInputDown = run;
// 更新动画
    var update = function ({
      // 清空
      context.clearRect(00, w, h);
      // 画转盘
      wheel.draw(context);
      // 画指针
      point.draw(context);

      // 更新数据
      animation.draw(context);
      // 更新数据
      animation.update();

      // 获取手指点击
      var touch = that.data.touch;
      if (point.inputEvent && touch.isPressed && point.onInputDown) {
        // 如果点击到了指针
        if (point.contains(touch)) {
          // 调用点击回调方法
          point.onInputDown();
        }
      }

      // 绘图   
      context.draw()

    };
    setInterval(update, 1000 / fps, 1000 / fps);

点击指针之后的回调处理
使用setTimeout 模拟后端返回结果
使用stopTo 方法来让大转盘停止到某一个奖品

 // 开始转
    function run({
      // 避免重复调用
      if (animation.isRun) return;
      // 当动画完成时
      animation.onComplete = function (prize{
        wx.showToast({
          image: prize.img,
          title: prize.text,
          duration3000,
          masktrue,
        })
      };

      // 开始转
      animation.run();

      // 模拟后台返回数据
      setTimeout(function ({
        // 随机一个奖品
        var prizeIndex = utils.getRandom(slicePrizes.length - 1);
        // 计算奖品角度
        animation.stopTo(prizeIndex);
      }, 3000);
    }

转动处理的构造方法

function Animation(circle, screen{
  this.circle = circle;
  this.screen = screen;
  // 角速度
  this.speed = 0;
  // 最大速度
  this.maxSpeed = 10;
  // 摩擦力
  this.friction = 0.98;
  // 加速度
  this.acceleration = 0.5;
  // 是否开始运行
  this.isRun = false;
  // 圈数
  this.rounds = 5;
  // 角度
  this.degrees = 0;
  // 当前角度
  this.angle = -90;
  // 开始减速
  this.speedDown = false;
  // 开始加速
  this.speedUp = true;
  // 顺时针还是逆时针
  this.anticlockwise = false;
  // 完成
  this.onComplete = null;
  // debug
  this.isDebug = false;
}

转动效果,先加速》匀速一段时间》调用stopTo方法后》减速》停止

Animation.prototype.update = function ({
  if (this.isRun) {
    // 到达奖品区
    var isJoin = this.prize && this.angle > this.minAngle && this.angle < this.maxAngle;
    // 是否要减速
    if (isJoin) {
      this.speedDown = true;
    }

    // 是否要停止加速
    if (this.speed >= this.maxSpeed) {
      this.speedUp = false;
    }

    // 加速
    if (this.speedUp) {
      this.speed += this.acceleration;
    }

    // 减速
    if (this.speedDown) {
      if (this.speed <= 2 && isJoin) {
        this.isRun = false;
        this.speed = 0;
        if (this.onComplete) this.onComplete(this.prize);
      } else if (this.speed <= 1) {
        this.speed = 1;
      } else {
        this.speed *= this.friction;
      }
    }

    this.angle += this.speed;
    // 转动角度
    if (Math.abs(this.angle) > 360) {
      this.angle -= 360;
    }
    // 旋转方向
    if (this.anticlockwise) {
      this.circle.rotation += (Math.PI / 180) * this.speed;
    } else {
      this.circle.rotation -= (Math.PI / 180) * this.speed;
    }
  }
};

大转盘的绘制

Circle.prototype.draw = function (context{
  // 保存
  context.save();
  // 移动到圆心
  context.translate(this.x, this.y);
  // 旋转
  context.rotate(this.rotation);
  // 缩放
  // context.scale(this.scaleX, this.scaleY);

  if (this.img) {
    var imgX = -this.width / 2;
    var imgY = -this.height / 2;
    context.drawImage(this.img, imgX, imgY);
  }
  var colors = this.colors;
  var size = this.size;
  var angle = this.angle;

  var r = this.radius;

  // 画扇形
  for (var i = 0; i < size; i++) {
    context.beginPath()
    context.moveTo(00)
    context.arc(00, r, 0, angle * Math.PI / 180)
    context.setFillStyle(colors[i % colors.length])
    context.fill()
    context.rotate(angle * Math.PI / 180);
  }

  // 画图案
  // 计算圆上任意一点
  var offset = this.radius / 2;
  var offsetA = angle / 2;
  var offsetX = Math.cos(offsetA * Math.PI / 180) * offset;
  var offsetY = Math.sin(offsetA * Math.PI / 180) * offset;
  for (var i = 0; i < size; i++) {
    var prize = this.slicePrizes[i]
    context.save();

    context.translate(offsetX, offsetY);

    context.rotate((180 - ((180 - angle) / 2)) * Math.PI / 180);
    context.drawImage(prize.img, -10-30this.prizeWidth, this.prizeHeight);

    // 写文字
    context.setFontSize(this.prizeFontSize)
    context.setFillStyle(this.prizeFontStyle)
    context.textAlign = "center";
    context.fillText(prize.text, 0, (offset - 25) * -1)

    // 话圆点参考
    // context.beginPath()
    // context.arc(0, 0, 2, 0, 2 * Math.PI)
    // context.setFillStyle('red')
    // context.fill()

    context.restore()
    context.rotate(angle * Math.PI / 180);
  }

  // 还原
  context.restore();
};

指针和跑马灯的绘制

Circle.prototype.draw = function (context{
  // 保存
  context.save();
  // 移动到圆心
  context.translate(this.x, this.y);
  // 旋转
  context.rotate(this.rotation);
  // 缩放
  // context.scale(this.scaleX, this.scaleY);

  if (this.img) {
    var imgX = -this.width / 2;
    var imgY = -this.height / 2;
    context.drawImage(this.img, imgX, imgY);
  }

  // 指针
  context.beginPath()
  context.setFillStyle('#ffffff')
  context.moveTo(0, (this.radius + 50) * -1);
  context.lineTo(-100);
  context.lineTo(100);
  context.fill()

  // 圆盘
  context.beginPath()
  context.setFillStyle('#ffffff')
  context.arc(00this.radius, 02 * Math.PI)
  context.fill()

  // 文字
  context.setFontSize(30)
  context.setFillStyle('#ff2244')
  context.setTextAlign("center");
  context.fillText('抽奖'010)

  var r = this.wheel.radius;
  // 跑马灯外框  F6D000  FFA200
  context.beginPath()
  context.setLineWidth(15);
  context.setStrokeStyle("#FFA200")
  context.arc(00, r + 2002 * Math.PI)
  context.stroke()

  // 跑马灯内框 F6D000
  context.beginPath()
  context.setLineWidth(10);
  context.setStrokeStyle("#F6D000")
  context.arc(00, r + 802 * Math.PI)
  context.stroke()

  // 跑马灯灯  
  for (var i = 0; i < 32; i++) {
    context.beginPath()
    context.setFillStyle(i % 2 == 0 ? "#ffffff" : "#FF403A")
    context.arc(r + 200402 * Math.PI)
    context.fill()
    context.rotate((360 / 32) * Math.PI / 180);
  }
  // 还原
  context.restore();
};

完整代码

GitHub 地址: https://github.com/qiaohhgz/bigwheel-miniapp

关注我们


IT实战联盟是集产品、UI设计、前后端、架构、大数据和AI人工智能等为一体的实战交流服务平台!联盟嘉宾都为各互联网公司项目的核心成员,联盟主旨是“让实战更简单”,欢迎来撩~~~

我们的网站: http://100boot.cn



全部评论: 0

    我有话说:

    程序电商实战-首页(上)

    上一篇:程序电商实战-入门篇 嗨,大家好!经过近两周的精心准备终于开始程序电商实战路喽。那么最终会做成什么样呢?好了,不啰嗦了 我们先看首页长什么样吧!   首页效果图

    程序抖音实战-首页(上)

    你也可以用程序编写一个抖音

    程序实战篇:程序页面数据传递

    我们在写程序的时候经常会遇到子页面向主页面回传数据或者普通页面跳转到tabBar 页面携带数......

    程序 - iconfont 图标字体

    你还在使用图片作为程序的图标?猪告诉大家如何在程序上使用iconfont字体图标

    程序实现商品数量加减

    这是一个用程序原生代码实现的数量加减demo,主要是用于商品购物车或者商品详情修改数量使用

    程序商城(七):动态API实现商品分类

    程序商品分类页面布局并且调用动态API获取数据并加载

    程序电商实战-首页(下)

    上一篇:程序电商实战-首页(上)好了,上一期我们把首页搜索、导航栏和广告轮播给做完了,那么接下来会继续

    程序实战篇:基于wxcharts.js绘制移动报表

    程序图表插件(wx-charts)是基于canvas绘制,体积巧,支持图表类型饼图、线图、柱状图 ......

    程序-欢乐夹娃娃

    前言 夹娃娃营销活动大家应该都不陌生那如何用程序实现呢?今天就带大家用canvas撸了一个全手工绘制的夹娃娃分享给大家。此处应有掌声 : ) 一、效果图 、项目结构 三、运行流程 获取

    程序电商实战-商品列表流式布局

    今天给大家分享一下程序中商品列表的流式布局方式,根据文章内容操作就可以看到效果哦~~~

    程序-Image的widthFix属性和rpm尺寸的使用

    在做程序的商品详情页,商品的详情是图片集合,渲染完成后发现图片加载的很不自然

    程序实战篇:如何解决https域名问题 ?

    开发自己的程序绕不开https问题,为了能在程序中调用我们自己的API服务请打开看一看吧!!!

    程序实战-幸运转盘

    程序幸运转盘

    程序实战篇:商品属性联动选择(案例)

    本期的程序实战篇来做一个电商网站经常用到的-商品属性联动选择的效果,素材参考了一点点

    程序商城(九):授权并实现个人中心页面页面

    实现商城的授权并获取用户信息和个人中心页面布局

    程序抖音实战-首页(下)

    抖音程序首页动态数据获取

    程序电商实战-入门篇

    程序开发工具有新版本更新啦!开发体验更好了,接下来一起为电商程序做一下准备前期准备工作~~

    程序电商实战-商品详情(上)

    先看一下今天要实现的程序商品详情页吧!