08_微信小程序之大转盘svg实现
一.关于微信小程序对svg的支持
这里先准备一个静态的svg文件做测试
<!--components/turntable-svg/images/secs.svg-->
<svg width="320" height="320" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g transform="rotate(-67.5,160,160)"><path fill="rgb(215,215,215)" d=" M160, 160 L273.1370849898476, 46.86291501015236 A160, 160 0 0, 1 320, 159.99999999999997 z"></path></g>
<g transform="rotate(-22.5,160,160)"><path fill="rgb(254,244,61)" d=" M160, 160 L273.1370849898476, 46.86291501015236 A160, 160 0 0, 1 320, 159.99999999999997 z"></path></g>
<g transform="rotate(22.5,160,160)"><path fill="rgb(239,119,131)" d=" M160, 160 L273.1370849898476, 46.86291501015236 A160, 160 0 0, 1 320, 159.99999999999997 z"></path></g>
<g transform="rotate(67.5,160,160)"><path fill="rgb(215,215,215)" d=" M160, 160 L273.1370849898476, 46.86291501015236 A160, 160 0 0, 1 320, 159.99999999999997 z"></path></g>
<g transform="rotate(112.5,160,160)"><path fill="rgb(254,244,61)" d=" M160, 160 L273.1370849898476, 46.86291501015236 A160, 160 0 0, 1 320, 159.99999999999997 z"></path></g>
<g transform="rotate(157.5,160,160)"><path fill="rgb(239,119,131)" d=" M160, 160 L273.1370849898476, 46.86291501015236 A160, 160 0 0, 1 320, 159.99999999999997 z"></path></g>
<g transform="rotate(202.5,160,160)"><path fill="rgb(215,215,215)" d=" M160, 160 L273.1370849898476, 46.86291501015236 A160, 160 0 0, 1 320, 159.99999999999997 z"></path></g>
<g transform="rotate(247.5,160,160)"><path fill="rgb(254,244,61)" d=" M160, 160 L273.1370849898476, 46.86291501015236 A160, 160 0 0, 1 320, 159.99999999999997 z"></path></g>
</svg>
那么接下来我们用image标签来显示下服务器端的一个svg,这里我把上述的svg放到了ftp上
<!--index.wxml-->
<view class="container">
<image style="width:600rpx; height: 600rpx;" src="{{ftp_director}}/secs.svg"></image>
</view>
可以看到,微信小程序的image标签是能够正常显示svg类型的网络图片的
接下来,我们把svg文件放到小程序静态资源下,再用image加载看下
<!--index.wxml-->
<view class="container">
<image style="width:600rpx; height: 600rpx;" src="../../components/turnable-svg/images/secs.svg"></image>
</view>
可以看到,同样也是能够正常显示的
那么问题来了,实现大转盘功能需要我们根据奖品数量去动态生成扇形数量,组成圆盘,通过上述两种办法明显达不到我们要的效果,那么应该怎么办呢?那么我们可不可以将svg的内容直接设置在image标签上类似base64的方式显示出图片呢,答案当然是可以😄,那么我们继续,接下来才是重点哦
<!--index.wxml-->
<view class="container">
<image style="width:600rpx; height: 600rpx;" src="{{svg}}"></image>
</view>
//index.js
//获取应用实例
const app = getApp()
Page({
data: {
svgRawStr: '<svg width="320" height="320" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g transform="rotate(-67.5,160,160)"><path fill="rgb(215,215,215)" d=" M160, 160 L273.1370849898476, 46.86291501015236 A160, 160 0 0, 1 320, 159.99999999999997 z"></path></g><g transform="rotate(-22.5,160,160)"><path fill="rgb(254,244,61)" d=" M160, 160 L273.1370849898476, 46.86291501015236 A160, 160 0 0, 1 320, 159.99999999999997 z"></path></g><g transform="rotate(22.5,160,160)"><path fill="rgb(239,119,131)" d=" M160, 160 L273.1370849898476, 46.86291501015236 A160, 160 0 0, 1 320, 159.99999999999997 z"></path></g><g transform="rotate(67.5,160,160)"><path fill="rgb(215,215,215)" d=" M160, 160 L273.1370849898476, 46.86291501015236 A160, 160 0 0, 1 320, 159.99999999999997 z"></path></g><g transform="rotate(112.5,160,160)"><path fill="rgb(254,244,61)" d=" M160, 160 L273.1370849898476, 46.86291501015236 A160, 160 0 0, 1 320, 159.99999999999997 z"></path></g><g transform="rotate(157.5,160,160)"><path fill="rgb(239,119,131)" d=" M160, 160 L273.1370849898476, 46.86291501015236 A160, 160 0 0, 1 320, 159.99999999999997 z"></path></g><g transform="rotate(202.5,160,160)"><path fill="rgb(215,215,215)" d=" M160, 160 L273.1370849898476, 46.86291501015236 A160, 160 0 0, 1 320, 159.99999999999997 z"></path></g><g transform="rotate(247.5,160,160)"><path fill="rgb(254,244,61)" d=" M160, 160 L273.1370849898476, 46.86291501015236 A160, 160 0 0, 1 320, 159.99999999999997 z"></path></g></svg>',
svg: ""
},
onLoad: function() {
var svgRawStr = this.data.svgRawStr
var svg = "data:image/svg+xml," + svgRawStr
this.setData({
svg: svg
})
}
})
可以看到,同样也是支持的,微信小程序的image标签还是挺强大的
既然我们可以将svg的内容作为一个字符串正常显示在image标签上,而这个字符串我们是可以通过设置的奖品去动态生成的,从而达到我们想要的效果,通过奖品动态生成圆盘
好了,既然有了思路,那我们就基于svg来一步一步实现大转盘的自定义组件
二.界面布局
<!--components/turnable-svg/index.wxml-->
<view class="turntable" style="width:{{size}}rpx; height:{{size}}rpx;">
<image class="outer" src="./images/outer.png"></image>
<view class="secs"></view>
<image class="start" src="./images/start.png"></image>
</view>
/* components/turnable-svg/index.wxss */
.turntable {
position: relative;
margin-left: 50%;
transform: translateX(-50%);
}
.outer {
width: 100%;
height: 100%;
}
.secs {
position: absolute;
left: 40rpx;
top: 40rpx;
width: calc(100% - 80rpx);
height: calc(100% - 80rpx);
}
.start {
position: absolute;
width: 112rpx;
height: 124rpx;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
z-index: 99;
}
// components/turnable-svg/index.js
Component({
/**
* 组件的属性列表
*/
properties: {
size: {
type: Number,
value: 600,
observer: function(newVal, oldVal) {
this.setData({
size: newVal
})
}
},
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
}
})
三.根据奖品个数绘制扇形组成圆盘
svg绘制规则
<path fill="rgb(215,215,215)" d=" M160, 160 L273.1370849898476, 46.86291501015236 A160, 160 0 0, 1 320, 159.99999999999997 z"></path>
<!--参数:
fill是填充的颜色
在d中,如何是绘画半圆的画,
M x,y其中的x,y表述圆心的坐标
L x1,y1标识从x,y到x1,y1的直线。
A表示绘画扇形
A rx,ry ratation_deg,flag1,flag2,x2,y2
其中rx表示x轴的半径
ry表示y轴的半径
ratation_deg 表示绘画的旋转角度,一般为0,
flag1,flag2表示绘画是按照大,还是按照小,也就是按照顺时针还是逆时针来进行绘画。x2,y2和x,y联成的直线为开始线,按照顺时针或者逆时针进行绘画,当和x,y和x1,y1连成的直线为终结线,绘画结束。z表示绘画的路径要闭合,同时file="none"。-->
确定第一个扇形(与圆右端点重和的扇形),假设扇形半径为r,圆形坐标为c(x, y),夹角为alpha,其中一个端点为(2r,r),则另一个端点坐标为
(cx + r*cos(alpha), cy - r*sin(alpha))
<!--components/turnable-svg/index.wxml-->
<!--components/turnable-svg/index.wxml-->
<view class="turntable" style="width:{{size}}rpx; height:{{size}}rpx;">
<image class="outer" src="./images/outer.png"></image>
<view class="secs" style="background-size: cover; background-image: url('{{svg}}')"></view>
<image class="start" src="./images/start.png"></image>
</view>
// components/turnable-svg/index.js
Component({
/**
* 组件的属性列表
*/
properties: {
size: {
type: Number,
value: 600,
observer: function(newVal, oldVal) {
this.setData({
size: newVal
})
}
},
prizeList: {
type: Array,
value: [],
observer: function(newVal, oldVal) {
this.setData({
prizeList: newVal
})
this.initTurntable()
}
}
},
lifetimes: {
ready: function() {
this.initTurntable()
}
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
initTurntable: function() {
var that = this
const query = that.createSelectorQuery()
query.select('.secs')
.fields({ node: true, size: true })
.exec((res) => {
var size = res[0].width
//圆心坐标
var centerX = res[0].width/2
var centerY = res[0].height/2
that.setData({
centerX: centerX,
centerY: centerY
})
//半径
var radius = size/2
//奖品数量
var num = that.data.prizeList.length
// 扇形旋转转角度
var rotateDeg = 360 / num / 2 - 90
var colors = ['#D7D7D7', '#FEF43E', '#EF7683']
//参数d
var mx = centerX
var my = centerY
var lx = size
var ly = centerY
var ax = centerX
var ay = centerY
var deg = 0
var flag1 = 0
var flag2 = 1
var dx = centerX + radius * Math.cos((360 / num) * (Math.PI / 180))
var dy = centerY - radius * Math.sin((360 / num) * (Math.PI / 180))
var svgRawStr = `<svg width="${size}" height="${size}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">`
//绘制扇形
for(var i = 0; i < num; i++) {
var k = i%3
var rotate = 360 / num * i + rotateDeg
var sec = `<g transform="rotate(${rotate},${centerX},${centerY})"><path fill="${colors[k]}" d="M${mx}, ${my} L${lx}, ${ly} A${ax}, ${ay} 0 0, 0 ${dx}, ${dy} z"></path></g>`
svgRawStr += sec
}
svgRawStr += "</svg>"
var svg = "data:image/svg+xml," + encodeURIComponent(svgRawStr)
that.setData({
svg: svg
})
})
}
}
})