0

0

canvas实践小实例二 —— 扇形

黄舟

黄舟

发布时间:2017-02-25 11:59:19

|

2475人浏览过

|

来源于php中文网

原创

俗话说:发图不留种,菊花万人捅!我这里想延伸一下:教学不给例,说你是傻逼!哎呀,还挺押韵,嘻嘻,开个玩笑!

我们都讲了四期api的知识了,估计大家看的也是枯燥的很啊,前面的小实例也是太简单,简直不解渴啊,但是也不能一口气就吃成一个胖子,下面再给大家来一个小实例,给大家提提神!
前面在讲画圆的时候,给大家留了一个思考,或者说是一个坑吧,就是如何来画一个扇形?我们知道画圆的方法是无法一下子就能画出一个扇形的,我当时提供了一个方法,不知道大家是否有印象,没印象没关系,我再复述一遍:就是如果我画了一个圆弧,然后在圆心画2条线,分别连在圆弧的起始点和结束点,那么这不就是一个圆吗?那么这个方法到底能不能画出一个圆呢?其实我也不知道,那我们就试一下:
第一步,画一个圆弧:

//将原点移到100,100的位置
ctx.translate(100, 100);
//画一个圆弧
ctx.arc(0,0,100,30*Math.PI/180, 60*Math.PI/180);
ctx.stroke();


canvas实践小实例二  —— 扇形

此时就是我们熟悉的画圆弧的方法,现在要开始画线了,这才是关键,我们先分析一下,磨刀不费砍柴工嘛!

一条直线是由2个点构成,现在我们知道圆心这点,第2个点就是圆弧的起始点和结束点,那么这2的坐标我们怎么得到呢?我们画图来分析一下:

canvas实践小实例二  —— 扇形

我们大概就是要画这样一个扇形,画的比较丑,将就看一下,如果我们按照数学思路,根据角度公式一通算,或许能得到这2个点的坐标,但是我感觉我自己想想都觉得这运算该有多么的复制,我数学不好,不愿意算,有没有一种简便的方法呢,可以不用那么的懂脑筋的?(抱歉,我的“内存”明显感到不足)我有一个大胆的假设,就是如果这个扇形不是这样斜着的,而是一边的水平的,比如说这样:

canvas实践小实例二  —— 扇形



那么我就可以很容易的得到第一条线段了,就是横坐标上的那条线,你不懂?好吧,是这样的,圆心坐标我们知道,半径我们知道,那弧的起点坐标就很容易啦,懂了吧,那另一条线怎么弄呢?如果你刚看完上一期API的知识,我们趁热,很容易就能想到rotate()方法,就是我们再画一条圆心到起点的线,圆弧的角度我们是知道的,好,我们将新画的这条线选择圆弧的角度,那不就到终点了吗?我靠,我TM太机智了!我们来试一下:

//圆弧
ctx.save();
ctx.translate(100, 100);
ctx.arc(0,0,100,0, 30*Math.PI/180);
ctx.restore();
//第一条线
ctx.save();
ctx.moveTo(100,100);
ctx.lineTo(200,100);
ctx.restore();
//第二条线
ctx.save();
ctx.translate(100, 100);
ctx.moveTo(0,0);
ctx.rotate(30*Math.PI/180);
ctx.lineTo(100,0);
ctx.stroke();
ctx.restore();


canvas实践小实例二  —— 扇形

哇塞,简直眼前一亮啊,果然可以啊,按照这思路,如果我们现在把这个扇形旋转一个角度,这个角度=第二条线的角度-第一条线的角度,那么得到的不就是前面我们需要的那个扇形吗?但是前面的代码直接旋转只能旋转圆弧,线旋转不了,我们修改一下:


canvas实践小实例二  —— 扇形

还是看这张图吧,我们可以换一个思路,如果圆弧画在目标位置,然后画2条角度为0的2条线,再旋转到圆弧的起始点和结束点,不就行了吗?(因为圆弧的起始角度和结束角度我们是知道的)试试吧:

//将原点设置100,100位置
ctx.translate(100,100);
//原点在100,100,则圆心设为0,0 ——> 100,100的位置
ctx.arc(0,0,100,30*Math.PI/180,60*Math.PI/180);
//save(),restore()是为了防止角度旋转的污染
ctx.save();
ctx.rotate(30*Math.PI/180);
ctx.moveTo(0,0);
ctx.lineTo(100,0);
ctx.restore();
ctx.rotate(60*Math.PI/180);
ctx.moveTo(0,0);
ctx.lineTo(100,0);
ctx.stroke();

canvas实践小实例二  —— 扇形

哎呀,真的可以啊,哈哈,有人会问,你的第一步为什么是设置原点呢,为什么不用moveTo来设置起始点呢?好问题,因为画布的默认原点在0,0的位置上,如果用moveTo来设置起始点,原点依然还在0,0的位置,上一节API我们将变换的时候讲到,变换是以原点为基准点的,即使你设置了起始点,但是起始点不是原点的话,图形旋转依然会围绕0,0点旋转然后自转,得到的图形就不知道是什么图形了,偏差的角度就很难矫正,对此还是不太明白的同学可以自己写一个例子体验一样,或许理解更深刻一点,这里就作为练习题,不在这里写了!

上面的代码还是可以优化的,比如说画第一条线的时候,我们用到了save()和restore(),其作用不只是可以防止外面的属性或方法对里面的绘制产生影响,它的本质意思是save()保存当前环境的状态,restore()返回之前保存路径的状态,这是什么意思,举个栗子,save()就像是在一个迷宫的入口,restore()就想是这个迷宫的出口,但是发现这里就是迷宫的路口,出了迷宫,在迷宫里具体是怎么走的,根本不知道,这就可以防止你的外部因素来影响你走迷宫的路线,那有一个细节大家要注意,就是当你进去的这个门,你出来的时候还是这个门,恩,这个就可以利用了,这就相当于是画笔的触点了,还原触点,我们看一下还原的触点在什么地方:

//将原点设置100,100位置
ctx.translate(100,100);
//原点在100,100,则圆心设为0,0 ——> 100,100的位置
ctx.arc(0,0,100,30*Math.PI/180,60*Math.PI/180);
//save(),restore()是为了防止角度旋转的污染
ctx.save();
ctx.rotate(30*Math.PI/180);
ctx.moveTo(0,0);
ctx.lineTo(100,0);
ctx.restore();
ctx.rotate(60*Math.PI/180);
ctx.lineTo(100,0);
ctx.stroke();



canvas实践小实例二  —— 扇形

canvas实践小实例二  —— 扇形

居然得到的是这样的结果,从第2张图可以看出还原的触点的位置在圆弧的初始点,其实这里我们是忽略了一个问题,就是线在旋转的时候,是从它的起点为圆心旋转的,而上面的代码是,第一条线从圆心开始,到圆弧的起点(旋转过后),自然现在的起点就是圆弧的起点了,第二条线怎么画,它旋转的结果都不是我们想要的了,所以这里我们需要特别的注意,现在我们将第一条直线的起点设在(r,0)的位置,旋转后就到了圆弧的起始点,然后在画到圆心地方,那现在的起始点就是圆心了,再画一条线到圆弧,就哦了,现在我们再来一次:

//将原点设置100,100位置
ctx.translate(100,100);
//原点在100,100,则圆心设为0,0 ——> 100,100的位置
ctx.arc(0,0,100,30*Math.PI/180,60*Math.PI/180);
//save(),restore()是为了防止角度旋转的污染
ctx.save();
ctx.rotate(30*Math.PI/180);
ctx.moveTo(100,0);
ctx.lineTo(0,0);
ctx.restore();
ctx.rotate(60*Math.PI/180);
ctx.lineTo(100,0);
ctx.stroke();


canvas实践小实例二  —— 扇形

看,这就是我们想要的图形,所以,上面所犯的几个错都是比较容易犯的错,需要特别的注意!

根据这个原理,我们其实还可以用另外一种方式,就是充分使用触点的作用,怎么讲,当我们再画圆弧的时候,画完之后其触点在圆弧的结束位置,如此的天赐良机,为何不直接将这个触点作为起点,画一条到圆心的线,不就可以少旋转一次吗?然后再画第二条线,简直感觉省时省力,我们看看效果吧:

//将原点设置100,100位置
ctx.translate(100,100);
//原点在100,100,则圆心设为0,0 ——> 100,100的位置
ctx.arc(0,0,100,30*Math.PI/180,60*Math.PI/180);
//以圆弧终点为起点画直线
ctx.lineTo(0,0);
ctx.rotate(30*Math.PI/180);
//以0,0为起点画直线
ctx.lineTo(100,0);
ctx.stroke();


canvas实践小实例二  —— 扇形

你看,用这个理论,就连save(),restore()都可以省了,因为就只有一个旋转,代码也少了好多,效果还一样,哈哈,为了能重复使用,我们需要把他封装一下:

第一种:

CanvasRenderingContext2D.prototype.sector = function(x,y,r,sDeg,eDeg){
            this.save();
            this.translate(x,y);
            this.beginPath();
            this.arc(0,0,r,sDeg*Math.PI/180,eDeg*Math.PI/180);
            this.save();
            this.rotate(sDeg*Math.PI/180);
            this.moveTo(r,0);
            this.lineTo(0,0);
            this.restore();
            this.rotate(eDeg*Math.PI/180);
            this.lineTo(r,0);
            this.restore();
            return this;
        }
        ctx.sector(100,100,100,30,60).stroke();
        ctx.sector(100,100,100,90,120).fill();
        ctx.sector(100,100,100,160,180).stroke();



canvas实践小实例二  —— 扇形

第二种:

CanvasRenderingContext2D.prototype.sector = function(x,y,r,sDeg,eDeg){
            this.save();
            this.translate(x,y);
            this.beginPath();
            this.arc(0,0,r,sDeg*Math.PI/180,eDeg*Math.PI/180);
            this.lineTo(0,0);
            this.rotate(sDeg*Math.PI/180);
            this.lineTo(r,0);
            this.restore();
            return this;
        }
        ctx.sector(100,100,100,30,60).stroke();
        ctx.sector(100,100,100,90,120).fill();
        ctx.sector(100,100,100,160,180).stroke();


canvas实践小实例二  —— 扇形

你以为这样就完了吗?当我们充分理解canvasAPI的基础知识的时候,我们还会得到另外一种方式来画扇形,简直6到爆!哈哈哈!究竟是什么呢?我们接着往下看:

前面的基础知识讲到画圆的时候,我们讲到了beginPath()和closePath(),有人会说,这不就是开始路径和封闭路径吗?这跟画扇形有什么关系?没错,你说的一点都没错,好,现在请大声跟我念:封闭路径!封闭路径!封闭路径!重要事情说3遍,现在你的心里是不是有了那么一点感觉,没错,不要觉得害羞,不要觉得压抑,就是它,就是它,大声把它说出来,就是这感觉,什么?你什么感觉都没有,此处有表情,好吧,我来告诉你我的感觉:

上面有一处说,为什么要用translate,而不要moveTo,是因为我们需要旋转,所以就需要原点,现在如果我们不需要旋转,而是正常的画图,那么我们就不需要原点,我们就可以用moveTo,好了,如果我们配合beginPath()和closePath(),就会将一个圆弧封闭起来,想想我们在讲画三角形的时候的那段折线是怎么变成三角形的,没错,现在是否有了一点感觉?还是木有?好吧,我们来看一个栗子:

ctx.beginPath();
//定义起点
ctx.moveTo(100,100);
//以起点为圆心,画一个半径为100的圆弧
ctx.arc(100,100,100,30*Math.PI/180, 60*Math.PI/180);
ctx.closePath();
ctx.stroke();


canvas实践小实例二  —— 扇形

看看,寥寥数行,就画出了一个扇形,对不上面的图像,是不是一样的?我们封装一下:

怪兽AI数字人
怪兽AI数字人

数字人短视频创作,数字人直播,实时驱动数字人

下载
CanvasRenderingContext2D.prototype.sector = function(x,y,r,angle1,angle2){
            this.save();
            this.beginPath();
            this.moveTo(x,y);
            this.arc(x,y,r,angle1*Math.PI/180,angle2*Math.PI/180,false);
            this.closePath();    
            this.restore();
            return this;
        }
        ctx.sector(100,100,100,30,60).stroke();
        ctx.sector(100,100,100,90,120).fill();
        ctx.sector(100,100,100,160,180).stroke();

canvas实践小实例二  —— 扇形

效果都一样,只是思路不一样,或许还有别的方式来画扇形,如果大家有更好的方法,希望能留下你的代码,大家互相学习一下!

扇形的方法有了,具体用哪个可以依据自己的喜好,我就按照第3种来写一个小应用,饼图:

CanvasRenderingContext2D.prototype.sector = function(x,y,r,angle1,angle2){
            this.save();
            this.beginPath();
            this.moveTo(x,y);
            this.arc(x,y,r,angle1*Math.PI/180,angle2*Math.PI/180,false);
            this.closePath();    
            this.restore();
            return this;
        }
        ctx.fillStyle = 'red';
        ctx.sector(200,200,100,30,150).fill();
        ctx.fillStyle = 'green';
        ctx.sector(200,200,100,150,270).fill();
        ctx.fillStyle = 'blue';
        ctx.sector(200,200,100,270,390).fill();


canvas实践小实例二  —— 扇形

再写一个扇形倒计时:

CanvasRenderingContext2D.prototype.sector = function(x,y,r,angle1,angle2){
            this.save();
            this.beginPath();
            this.moveTo(x,y);
            this.arc(x,y,r,angle1*Math.PI/180,angle2*Math.PI/180,false);
            this.closePath();    
            this.restore();
            return this;
        }
        
        var angle = 0;
        var timer = null;
        ctx.fillStyle = 'green';
        setInterval(function(){
            angle+=5;
            ctx.sector(200,200,100,0,angle).fill();
            if(angle == 360){
                clearInterval(timer);  
            }
        },200);


canvas实践小实例二  —— 扇形

你以为我只是写几个例子给你看吗?你还是太年轻了,之所以要丢出这2个例子,是为了扩展一下思路,我们可以在这些效果上面加一点什么东西,效果是否就不一样了,举个例子,第一个饼图,如果我们在中间加一个白色的圆,会怎么样?

CanvasRenderingContext2D.prototype.sector = function(x,y,r,angle1,angle2){
            this.save();
            this.beginPath();
            this.moveTo(x,y);
            this.arc(x,y,r,angle1*Math.PI/180,angle2*Math.PI/180,false);
            this.closePath();    
            this.restore();
            return this;
        }
        ctx.fillStyle = 'red';
        ctx.sector(200,200,100,30,150).fill();
        ctx.fillStyle = 'green';
        ctx.sector(200,200,100,150,270).fill();
        ctx.fillStyle = 'blue';
        ctx.sector(200,200,100,270,390).fill();
        ctx.fillStyle = '#fff';
        ctx.sector(200,200,80,0,360).fill();


canvas实践小实例二  —— 扇形

看,这效果是不是就变成另外一个效果了,比如说第二个效果,我们也加一个白色的圆,看有什么效果:

CanvasRenderingContext2D.prototype.sector = function(x,y,r,angle1,angle2){
            this.save();
            this.beginPath();
            this.moveTo(x,y);
            this.arc(x,y,r,angle1*Math.PI/180,angle2*Math.PI/180,false);
            this.closePath();    
            this.restore();
            return this;
        }

        var angle = 0;
        var timer = null;
        setInterval(function(){
            angle+=5;
            ctx.fillStyle = 'green';
            ctx.sector(200,200,100,0,angle).fill();
            ctx.fillStyle = '#fff';
            ctx.sector(200,200,80,0,360).fill();
            if(angle == 360){
                clearInterval(timer);    
            }
            
        },200);


canvas实践小实例二  —— 扇形

看看,这效果是不是可以做很多的效果,当然,因为没有加动画效果,现在的效果很生硬,需要大家来完善,只要你脑洞打开,其实扇形还是能做出很多非常炫酷的效果的,当然了,好的效果都是需要打磨的,在此只是抛砖引玉,如果大家有更好,更炫酷的效果,请不吝分享一下,今天就讲到这里,谢谢大家的支持!

 以上就是canvas实践小实例二  —— 扇形 的内容,更多相关内容请关注PHP中文网(www.php.cn)!

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
go语言 注释编码
go语言 注释编码

本专题整合了go语言注释、注释规范等等内容,阅读专题下面的文章了解更多详细内容。

2

2026.01.31

go语言 math包
go语言 math包

本专题整合了go语言math包相关内容,阅读专题下面的文章了解更多详细内容。

1

2026.01.31

go语言输入函数
go语言输入函数

本专题整合了go语言输入相关教程内容,阅读专题下面的文章了解更多详细内容。

1

2026.01.31

golang 循环遍历
golang 循环遍历

本专题整合了golang循环遍历相关教程,阅读专题下面的文章了解更多详细内容。

0

2026.01.31

Golang人工智能合集
Golang人工智能合集

本专题整合了Golang人工智能相关内容,阅读专题下面的文章了解更多详细内容。

1

2026.01.31

2026赚钱平台入口大全
2026赚钱平台入口大全

2026年最新赚钱平台入口汇总,涵盖任务众包、内容创作、电商运营、技能变现等多类正规渠道,助你轻松开启副业增收之路。阅读专题下面的文章了解更多详细内容。

76

2026.01.31

高干文在线阅读网站大全
高干文在线阅读网站大全

汇集热门1v1高干文免费阅读资源,涵盖都市言情、京味大院、军旅高干等经典题材,情节紧凑、人物鲜明。阅读专题下面的文章了解更多详细内容。

73

2026.01.31

无需付费的漫画app大全
无需付费的漫画app大全

想找真正免费又无套路的漫画App?本合集精选多款永久免费、资源丰富、无广告干扰的优质漫画应用,涵盖国漫、日漫、韩漫及经典老番,满足各类阅读需求。阅读专题下面的文章了解更多详细内容。

67

2026.01.31

漫画免费在线观看地址大全
漫画免费在线观看地址大全

想找免费又资源丰富的漫画网站?本合集精选2025-2026年热门平台,涵盖国漫、日漫、韩漫等多类型作品,支持高清流畅阅读与离线缓存。阅读专题下面的文章了解更多详细内容。

19

2026.01.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Canvas 绘制时钟
Canvas 绘制时钟

共7课时 | 1.5万人学习

HTML5新特性基础视频教程
HTML5新特性基础视频教程

共18课时 | 3.2万人学习

HTML5 Canvas 动画实战教程
HTML5 Canvas 动画实战教程

共28课时 | 6.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号