本文章讲解怎么实现这样一个螺旋转盘动态效果。不停旋转,箭头指向的扇形会变成高亮,整个转盘有个渐变效果,中间镂空。
首先准备如下三张图
三张图怎么利用? 思路大概下面所标示的。第一张和第三种是盖到第二张上的,第二张图作为填充圆形的颜色。
因为用canvas本身的shadow并不好实现这个效果,另外性能也不高。所以就直接利用了上面左侧的这张图。来呈现高亮选中的那个转盘扇形。
1. 加载图片并保存填充颜色
为了可以绘制出扇形上颜色渐变的效果,保证整体上是一张过渡性很好的渐变色盘。需要加载这三张图片,并绘制到一个离屏canvas上,借助createPattern,用canvas作为重复内容填充绘制路径,并保存下来用于后面绘制扇形填充使用。
1 initWheelImage() { 2 var intoCanvas = document.createElement("canvas");//创建canvas 3 var ctx = intoCanvas.getContext('2d'); 4 intoCanvas.width = this.width + this.position.x;//设置到正确的宽高 5 intoCanvas.height = this.height + this.position.y; 6 7 //将三张图片加载 8 let wheelSurfColor = new Image(); 9 wheelSurfColor.src = wheelSurfColorUrl; 10 let wheelSurfCoverImg = new Image(); 11 wheelSurfCoverImg.src = wheelSurfCoverUrl; 12 let innerGlowSector = new Image(); 13 innerGlowSector.src = innerGlowSectorUrl; 14 //使用promise将图片加载成功后执行绘制操作 15 let that = this; 16 let arrPromise = []; 17 arrPromise.push(Util.getImgLoadPromise(wheelSurfColor)); 18 arrPromise.push(Util.getImgLoadPromise(innerGlowSector)); 19 arrPromise.push(Util.getImgLoadPromise(wheelSurfCoverImg)); 20 Promise.all(arrPromise).then( 21 function (imgArr) { 22 let r = that.innerRadius + that.maxOuterR; 23 //将图片绘制到离屏canvas上 24 ctx.drawImage(wheelSurfColor, that.position.x + that.width / 2 - r, that.position.y + that.height / 2 - r, r * 2, r * 2); 25 //指定重复元素用来填充路径 26 //或者 ctx.createPattern(intoCanvas, 'no-repeat') 使用哪个canvas都行 27 that.wheelSurfColorPattern = that.layer.canvasContext.createPattern(intoCanvas, 'no-repeat'); 28 ctx.clearRect(0, 0, intoCanvas.width, intoCanvas.height) 29 30 //同上面几句代码作用一致 31 ctx.drawImage(wheelSurfCoverImg, that.position.x + that.width / 2 - r, that.position.y + that.height / 2 - r, r * 2, r * 2); 32 that.wheelSurfCoverPattern = that.layer.canvasContext.createPattern(intoCanvas, 'no-repeat'); 33 that.initWheel.call(that, innerGlowSector); 34 that.update.call(that) 35 } 36 ); 37 }
图片加载完成后,调用初始化转盘其他元素的方法。
2. 初始化转盘其他展示内容
展示内容包括:31个扇形块,31个文字,顶部扇形,中间圆环,中间文字,箭头,刻度线等。这里用group划分组利于管理控制。
绘制文字有性能问题,所以用到了离屏canvas。
1 initWheel(innerGlowSector: HTMLImageElement) { 2 let circlePerDis = 73 * this.scale; 3 let font18 = 18 * this.scale; 4 let font20 = 20 * this.scale; 5 //绘制30个扇形块 6 let i; 7 let renderStyle = new RenderStyle(true, false, this.wheelSurfColorPattern); 8 //30个扇形组成group 并且设置颜色渐变的效果 需要初始化31个这样转的时候才不会有空缺 9 this.sectorGroup = new Group(renderStyle, new RotateConfig()); 10 for (i = 1; i <= 36; i++) { 11 if (i >= 32) {//不需要做什么了 12 13 } else {//加31个扇形 14 this.addNewSector(i); 15 } 16 } 17 18 this.sectorGroup.setRotateCenter(this.position.x + this.width / 2, this.position.y + this.height / 2); 19 this.sectorGroup.rotateBy(-60); 20 this.group.add(this.sectorGroup); 21 22 //绘制扇形上面的文字 23 this.textGroup = new Group(new RenderStyle(), new RotateConfig()); 24 for (i = 1; i <= 36; i++) { 25 if (i >= 32) { 26 27 } else { 28 this.addNewText(i, i >= 17 ? "left" : "right"); 29 } 30 } 31 this.textGroup.setRotateCenter(this.position.x + this.width / 2, this.position.y + this.height / 2); 32 //优化text绘制方式 提高性能 begin 33 this.redrawTextCanvas(); 34 this.textCanvasImage = new CanvasImage(this.textCanvas, new RenderStyle(), new RotateConfig(), new RegionConfig()); 35 this.textCanvasImage.setRotateCenter(this.position.x + this.width / 2, this.position.y + this.height / 2); 36 this.textCanvasImage.rotateBy(-60); 37 this.group.add(this.textCanvasImage); 38 //优化text绘制方式 提高性能 end 39 40 //绘制顶部的扇形 镂空效果 41 let renderStyle2 = new RenderStyle(true, false, this.wheelSurfCoverPattern); 42 renderStyle2.setGlobalCompositeOperation("destination-out"); 43 let sector2 = new Circle(new CircleConfig(this.innerRadius + this.maxOuterR + this.radiusDiff, new Point(this.position.x + this.width / 2, this.position.y + this.height / 2), 0, 360), renderStyle2, new RotateConfig()); 44 sector2.setRotateCenter(this.position.x + this.width / 2, this.position.y + this.height / 2); 45 this.group.add(sector2); 46 47 //绘制顶部半个扇形,蓝颜色有加深部分 48 let sectorColorDeeprenderStyle = new RenderStyle(true, false); 49 let percentColorArr = new Array<PercentColor>(new PercentColor(0, "rgba(0,30,255,0)"), new PercentColor(0.5, "rgba(0,30,255,0.2)"), new PercentColor(1, "rgba(0,30,255,1)")); 50 sectorColorDeeprenderStyle.setLinearGradient(new LinearGradientConfig(this.position.x + this.width / 2, this.position.y + this.height / 2, this.position.x + this.width / 2 + this.innerRadius + this.maxOuterR / 2, this.position.y + this.height / 2, percentColorArr)); 51 let sectorColorDeep = new Sector(new SectorConfig(this.innerRadius + circlePerDis * 4, new Point(this.position.x + this.width / 2, this.position.y + this.height / 2), 270, 315), sectorColorDeeprenderStyle); 52 this.group.add(sectorColorDeep); 53 54 //绘制中间的圆 镂空效果 55 let renderStyle1 = new RenderStyle(true, false, "green");//随便什么颜色 56 renderStyle1.setGlobalCompositeOperation("destination-out"); 57 let circle = new Circle(new CircleConfig(this.innerRadius, new Point(this.position.x + this.width / 2, this.position.y + this.height / 2)), renderStyle1); 58 this.group.add(circle); 59 60 //环绕文字内部的圆 紧贴扇形 61 let circleLineRenderStyle = new RenderStyle(false, true, "", "", 1, false); 62 circleLineRenderStyle.setLinearGradient(new LinearGradientConfig(this.position.x, this.position.y, this.position.x + this.width, this.position.y, this.percentColorArr)) 63 let circleLine = new Circle(new CircleConfig(this.innerRadius, new Point(this.position.x + this.width / 2, this.position.y + this.height / 2), -30, 300, true), circleLineRenderStyle, new RotateConfig()); 64 circleLine.setRotateCenter(this.position.x + this.width / 2, this.position.y + this.height / 2); 65 circleLine.rotateBy(-60); 66 this.group.add(circleLine); 67 //绘制环绕文字的两个线条 68 let circleLineTop = new Circle(new CircleConfig(this.innerRadius / 3 * 2, new Point(this.position.x + this.width / 2, this.position.y + this.height / 2), 220, 320), new RenderStyle(false, true, "", "rgba(38,110,225,1)", 1, false)); 69 this.group.add(circleLineTop); 70 let circleLineBottom = new Circle(new CircleConfig(this.innerRadius / 3 * 2, new Point(this.position.x + this.width / 2, this.position.y + this.height / 2), 20, 160), new RenderStyle(false, true, "", "rgba(38,110,225,1)", 1, false)); 71 this.group.add(circleLineBottom); 72 73 //绘制内部文字 74 this.className = new Text(this.dataList.get(1).gradeName + "-" + this.dataList.get(1).subjectName, new Point(this.position.x + this.width / 2, this.position.y + this.height / 2 - 20), new TextConfig("Arial", font18, "normal", "normal"), new RenderStyle(true, true, "#00f6ff"), new RotateConfig()); 75 this.className.moveBy(-this.className.getWidth(this.layer.canvasContext) / 2, 0); 76 this.group.add(this.className); 77 this.articleName = new Text(Util.subString1(this.dataList.get(1).unitName, 28, "..."), new Point(this.position.x + this.width / 2, this.position.y + this.height / 2 + 20), new TextConfig("Arial", font20, "normal", "normal"), new RenderStyle(true, true, "#00f6ff"), new RotateConfig()); 78 this.articleName.moveBy(-this.articleName.getWidth(this.layer.canvasContext) / 2, 0); 79 this.group.add(this.articleName); 80 81 82