canvas 订阅
《canvas》是从2005年10月3日开始放映的24集日本动画,由F&C ZEXCS制作发行,讲述了拥有超人的绘画天分的男主角的故事。 展开全文
《canvas》是从2005年10月3日开始放映的24集日本动画,由F&C ZEXCS制作发行,讲述了拥有超人的绘画天分的男主角的故事。
信息
外文名
Canvas
制作·发行
F&C ZEXCS
放映日期
2005年10月3日
日文名
虹色のスケッチ
国    家
日本
总共集数
24
canvas剧情简介
拥有超人的绘画天分的男主角,却因某事故无法再次执起画笔,只能委身于某所大学,担任美术课的顾问。受男主角指导走向绘画之路的女主角,却因目睹双亲车祸身亡的现场,而在作画中无法使用红色。青梅竹马的女伴,可是高中时代的告白,却遭到男主角的拒绝,现在她又重新闯入了男主角的生活。 偶然的再会…… 三人之间,会发生什么?爱的前路,会走向哪里?
收起全文
精华内容
下载资源
问答
  • canvas2image.js

    2017-10-12 10:55:09
    canvas2image.js 将canvas转化为image的js插件,可以将画布内容下载实现截图功能。
  • html2Canvas演示

    2016-12-14 17:57:01
    html2Canvas可以截取网页中的整体或者某部分,然后输出为图片
  • HTML5 canvas绘制流程图

    2017-01-18 16:26:52
    HTML5 canvas绘制流程图,无需flash,支持形状伸缩、编辑文字、拖拽、节点连线功能,类似visio。
  • html2canvas.js--自定义高度

    热门讨论 2017-01-12 14:16:03
    修改html2canvas.js源码,可以解决截图不全问题.使用方式参考 http://blog.csdn.net/q2365921/article/details/54377479
  • 使用html2canvas技术把页面截取成图片形式保存到本地
  • html2canvas.js 下载

    千次下载 热门讨论 2016-04-13 09:23:02
    html2canvas.js 和html2canvas.min.js 以及应用示例
  • Canvas画布图片文字拼接合成,生成图片,输入内容,填写到图片上
  • html5+exif.js+canvas

    千次下载 热门讨论 2015-09-22 15:40:58
    实现手机端照片上传预览、压缩、旋转功能。 //获取照片方向角属性,用户旋转控制 EXIF.getData(file, function() { // alert(EXIF.pretty(this));... base64 = canvas.toDataURL("image/jpeg", 0.8);
  • HTML5 canvas实现转盘游戏

    热门讨论 2015-02-09 18:43:04
    HTML5之纯canvas实现转盘游戏,用到了canvas的旋转,平移,叠加,清除
  • html5 canvas实现手写签名

    热门讨论 2014-10-20 15:57:36
    使用html5 canvas标签实现手写 但是效果不是很好 没有笔锋 后续慢慢改进 感谢实现过程中帮助我的人
  • HTML5 Canvas实现雷达扫描动画特效

    千次下载 热门讨论 2014-09-25 14:27:22
    这是一款基于HTML5 Canvas...这款雷达扫描动画就是利用了canvas的灵活特性,利用js在canvas上绘制这些美丽的扫描线条。此外这款源码还提供了点击绘制的功能,用户可以根据自己的喜好绘制出新的类似雷达扫描特效的图形。
  • html5Canvas实现的球内波浪动画,包括每一步实现的源码,注释,方便修改使用
  • 使用HTML5 canvas 标签进行图片裁剪、旋转、缩放示例代码
  • 附件中实现了通过html2canvas截图百度echart。解压即可运行
  • Android canvas 画布详细讲解

    千次阅读 2019-07-22 19:20:10
    文章目录1、简介2、常用的绘画方法 draw* ...比较清楚详细的讲解画布的一些基础方法,一些参数的含义,让读者能够更清楚地使用 canvas 绘制想要的图形。 2、常用的绘画方法 draw* 待续····· ...

    1、简介

    比较清楚详细的讲解画布的一些基础方法,一些参数的含义,让读者能够更清楚地使用 canvas 绘制想要的图形。
    在这里插入图片描述

    1) 功能架构

    在这里插入图片描述

    2)MainActivity.java 文件

    主函数

    package myapplication21.lum.com.mycanvas;
    
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    }
    
    3) activity_main.xml 添加自己画的view 组件
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <myapplication21.lum.com.mycanvas.MyView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    
    </LinearLayout>
    
    4)MyView.java 自己画的view
    package myapplication21.lum.com.mycanvas;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.support.annotation.Nullable;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.View;
    
    public class MyView extends View {
        String TAG = "lum_ : ";
         Paint paint;
        public MyView(Context context) {
            super(context);
            Log.i(TAG,"1");
        }
    
        public MyView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            Log.i(TAG,"2");
            //就是通过修改画笔的一些参数设置
            paint = new Paint(); //新建一个画笔对象
            paint.setAntiAlias(true);//抗锯齿功能
            paint.setColor(Color.RED);  //设置画笔颜色
            paint.setStyle(Paint.Style.STROKE);//设置填充样式 中空
            paint.setStrokeWidth(10);//设置画笔宽度 ,单位px
        }
    
        public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            Log.i(TAG,"3");
    
        }
    
        public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
            Log.i(TAG,"4");
        }
     
        protected void onDraw(Canvas canvas) {
         // 在这里进行创建自定义组件的绘制
        }
    
    }
    
    

    2、常用的绘画方法 draw*

    1)drawCircle

    (1) 画笔设置 Paint.Style.STROKE 中空模式

    //画笔设置
            paint = new Paint(); //新建一个画笔对象
            paint.setAntiAlias(true);//抗锯齿功能
            paint.setColor(Color.RED);  //设置画笔颜色
            **paint.setStyle(Paint.Style.STROKE);//设置填充样式 中空**
            paint.setStrokeWidth(10);//设置画笔宽度 ,单位px
    
           //省略·····
        protected void onDraw(Canvas canvas) {
            /*
            * 圆心 x 点坐标  
             * 圆心 y 点坐标
             *圆心 半径
            * 使用的画笔
            * */
            canvas.drawCircle(100, 100, 50, paint);
        }
    
    

    画出一个 与那还宽度为 10 ,整体半径为50的 圆环
    在这里插入图片描述

    (2)画笔设置 Paint.Style.FILL 实心模式

            paint.setStyle(Paint.Style.FILL);//设置填充样式 完全
    

    会画出一个实心圆
    在这里插入图片描述

    2)drawRect

    1) 画笔设置 Paint.Style.STROKE 中空模式

            paint = new Paint(); //新建一个画笔对象
            paint.setAntiAlias(true);//抗锯齿功能
            paint.setColor(Color.RED);  //设置画笔颜色
            paint.setStyle(Paint.Style.STROKE);//设置填充样式
            paint.setStrokeWidth(10);//设置画笔宽度 ,单位px
           //省略·····
            //绘制矩形区域
            //参数 矩形相对 边框的上下左右位置
            rect = new RectF(200, 200, 600, 600);
    
        protected void onDraw(Canvas canvas) {
            //定义的矩形区域,画笔
            canvas.drawRect(rect, paint);
        }
    

    在这里插入图片描述

    2)画笔设置 Paint.Style.FILL 实心模式

            paint.setStyle(Paint.Style.FILL);//设置填充样式 完全
    

    在这里插入图片描述

    3) drawArc

    1) 画笔设置 Paint.Style.STROKE 中空模式

            paint = new Paint(); //新建一个画笔对象
            paint.setAntiAlias(true);//抗锯齿功能
            paint.setColor(Color.RED);  //设置画笔颜色
            paint.setStyle(Paint.Style.STROKE);//设置填充样式
            paint.setStrokeWidth(10);//设置画笔宽度 ,单位px
            //绘制矩形区域
            rect = new RectF(200, 200, 600, 600);
           //省略·····
           
            protected void onDraw(Canvas canvas) {
            canvas.drawRect(rect,paint); //我们把辅助位置的矩形画出来
            paint.setColor(Color.BLUE);
            canvas.drawArc(rect, //确定弧形区域使用到的矩形,可以看到弧线是内切矩形
                    0, //开始角度
                    90, //扫过的角度
                    false, //是否使用中心
                    paint);
    
        }
    
    
    

    在这里插入图片描述
    2)画笔设置 Paint.Style.FILL 实心模式,
    (1)

            paint.setStyle(Paint.Style.FILL);//设置填充样式
    
      canvas.drawArc(rect, //确定弧形区域使用到的矩形
                    0, //开始角度
                    90, //扫过的角度
                    true, //使用中心
                    paint);
    

    在这里插入图片描述
    (2)
    不使用中心

    canvas.drawArc(rect, //确定弧形区域使用到的矩形
            0, //开始角度
            90, //扫过的角度
            false, //是否使用中心
            paint);
    

    在这里插入图片描述

    4) drawColor
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.BLUE);
    }
    

    是直接将View显示区域用某个颜色填充满。
    在这里插入图片描述

    5)drawLine
            paint = new Paint(); //新建一个画笔对象
            paint.setAntiAlias(true);//抗锯齿功能
            paint.setColor(Color.RED);  //设置画笔颜色
            paint.setStyle(Paint.Style.FILL);//设置填充样式
            paint.setStrokeWidth(10);//设置画笔宽度 ,单位px
    
        protected void onDraw(Canvas canvas) {
            //画一条线 x轴起点 ,y 轴起点, x 轴终点,y 轴终点
            canvas.drawLine(100, 100, 600, 600, paint);
        }
    

    在这里插入图片描述

    6)drawOval

    (1)画笔填充设置 为 中空STROKE

            paint = new Paint(); //新建一个画笔对象
            paint.setAntiAlias(true);//抗锯齿功能
            paint.setColor(Color.RED);  //设置画笔颜色
            paint.setStyle(Paint.Style.STROKE);//设置填充样式
            paint.setStrokeWidth(10);//设置画笔宽度 ,单位px
    
            //绘制矩形区域
            rect = new RectF(200, 200, 1000, 600);
    
        protected void onDraw(Canvas canvas) {
            canvas.drawRect(rect,paint);//画出参考的矩形区域
           //矩形区域内切椭圆
            canvas.drawOval(rect, paint);
        }
    

    在这里插入图片描述
    (2) 画笔模式设置为填充

            paint.setStyle(Paint.Style.FILL);//设置填充样式
      //省略······
        protected void onDraw(Canvas canvas) {
            canvas.drawRect(rect,paint);//画出参考的矩形区域
            paint.setColor(Color.BLUE);
           //矩形区域内切椭圆
            canvas.drawOval(rect, paint);
        }
    

    在这里插入图片描述

    7)drawPosText
            paint = new Paint(); //新建一个画笔对象
            paint.setAntiAlias(true);//抗锯齿功能
            paint.setColor(Color.RED);  //设置画笔颜色
            paint.setStyle(Paint.Style.FILL);//设置填充样式
            paint.setStrokeWidth(10);//设置画笔宽度 ,单位px
            paint.setTextSize(50); //设置字体大小
    
    
    
        protected void onDraw(Canvas canvas) {
            //按照既定点 绘制文本内容  
            canvas.drawPosText("Hello", new float[]{
                    60, 60,   //第一个字符的xy位置
                    120,120, //第二个字符xy 位置
                    180, 180,
                    240, 240,
                    300, 300
            }, paint);
        }
    

    在这里插入图片描述

    8)drawRoundRect
            paint = new Paint(); //新建一个画笔对象
            paint.setAntiAlias(true);//抗锯齿功能
            paint.setColor(Color.RED);  //设置画笔颜色
            paint.setStyle(Paint.Style.FILL);//设置填充样式 FILL
            paint.setStrokeWidth(10);//设置画笔宽度 ,单位px
            //绘制矩形区域
            rect = new RectF(200, 200, 600, 600);
    
        protected void onDraw(Canvas canvas) {
            canvas.drawRoundRect(rect,
                    60, //x轴的半径
                    60, //y轴的半径
                    paint);
        }
    
    
    9) drawPath

    (1) 画笔填充设置 为 中空STROKE

            paint = new Paint(); //新建一个画笔对象
            paint.setAntiAlias(true);//抗锯齿功能
            paint.setColor(Color.RED);  //设置画笔颜色
            paint.setStyle(Paint.Style.STROKE);//设置填充样式
            paint.setStrokeWidth(10);//设置画笔宽度 ,单位px
    
            path = new Path(); //定义一条路径
            path.moveTo(100, 100); //移动到 坐标10,10
            path.lineTo(200, 300); //移动到 坐标200,300
            path.lineTo(500,600); 
    
        protected void onDraw(Canvas canvas) {
            canvas.drawPath(path, paint);
        }
    

    在这里插入图片描述

    (2) 画笔模式设置为填充

            paint.setStyle(Paint.Style.FILL);//设置填充样式
    

    在这里插入图片描述

    10) drawTextOnPath
            paint = new Paint(); //新建一个画笔对象
            paint.setAntiAlias(true);//抗锯齿功能
            paint.setColor(Color.RED);  //设置画笔颜色
            paint.setStyle(Paint.Style.STROKE);//设置填充样式
            paint.setStrokeWidth(10);//设置画笔宽度 ,单位px
            paint.setTextSize(80);//设置字体大小
    
            path = new Path(); //定义一条路径
            path.moveTo(100, 100); //移动到 坐标10,10
            path.lineTo(200, 300); //移动到 坐标200,300
            path.lineTo(500,600);
    
    
        protected void onDraw(Canvas canvas) {
            canvas.drawPath(path,paint);//画出路径参考线
            paint.setColor(Color.BLUE);
    
                    /*
           参数一  显示的文字,
           参数2 绘制的路径
           参数三   hOffset : 与路径起始点的水平偏移距离
           参数四 vOffset : 与路径中心的垂直偏移量
           参数 五 画笔
                    * */
            canvas.drawTextOnPath("Android CSDN ", path, 0, 100, paint);
    
        }
    

    在这里插入图片描述

    11)drawText
            paint = new Paint(); //新建一个画笔对象
            paint.setAntiAlias(true);//抗锯齿功能
            paint.setColor(Color.RED);  //设置画笔颜色
            paint.setStyle(Paint.Style.STROKE);//设置填充样式
            paint.setStrokeWidth(10);//设置画笔宽度 ,单位px
            paint.setTextSize(80);//设置字体大小
    
    
    
        protected void onDraw(Canvas canvas) {
            // 现实的文字, 起始点 x 坐标,起始点 Y 坐标, 画笔
            canvas.drawText("Hello World",300, 300, paint);// 画文本
    
        }
    
    

    在这里插入图片描述

    12)drawPoint
    paint = new Paint(); //新建一个画笔对象
    paint.setAntiAlias(true);//抗锯齿功能
    paint.setColor(Color.RED);  //设置画笔颜色
    paint.setStyle(Paint.Style.STROKE);//设置填充样式
    paint.setStrokeWidth(30);//设置画笔宽度 ,单位px
    
    protected void onDraw(Canvas canvas) {
        //  起始点 x 坐标,起始点 Y 坐标, 画笔
        canvas.drawPoint(300, 300, paint);//画一个点
    
    }
    

    在这里插入图片描述

    13) drawBitmap
            paint = new Paint(); //新建一个画笔对象
            paint.setAntiAlias(true);//抗锯齿功能
            paint.setColor(Color.RED);  //设置画笔颜色
            paint.setStyle(Paint.Style.STROKE);//设置填充样式
            paint.setStrokeWidth(30);//设置画笔宽度 ,单位px
    
            //将图片转化成 bitmap 对象
            bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.vx);
    
        protected void onDraw(Canvas canvas) {
    
    /**
     * 参数1:bitmap对象
     * 参数2:图像左边坐标点
     * 参数3:图像上边坐标点
     */
            canvas.drawBitmap(bitmap, 200,300, paint);
    
        }
    

    在这里插入图片描述

    14) svg drawable.draw(canvas)

    svg_flag 是 svg转化的 xml 图片 信息

           VectorDrawableCompat drawable = VectorDrawableCompat.create(getResources(), R.drawable.svg_flag, null);
           drawable.setBounds(0, 0, 100,100);
           drawable.draw(canvas);
    

    在这里插入图片描述

    3、画布的旋转

    对于画布的旋转我们掌握一下三个函数:

    canvas.save(); //画布将当前的状态保存
    canvas.rotate(); //讲画布旋转一定角度
    canvas.restore(); //恢复画布到上一个保存的状态

    canvas.save();与canvas.restore();一般结合使用,.save()函数在前,.restore()函数在后,用来保证在这两个函数之间所做的操作不会对原来在canvas上所画图形产生影响。

    (1) 我们先画一个图片–>保存画布—>旋转画布—>画一个图片----->恢复画布---->再画一个图片

            paint = new Paint(); //新建一个画笔对象
            paint.setAntiAlias(true);//抗锯齿功能
            paint.setColor(Color.RED);  //设置画笔颜色
            paint.setStyle(Paint.Style.STROKE);//设置填充样式
            paint.setStrokeWidth(30);//设置画笔宽度 ,单位px
    
            //将图片转化成 bitmap 对象
            bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.vx);
    
        protected void onDraw(Canvas canvas) {
    
            /**
             * 参数1:bitmap对象
             * 参数2:图像左边坐标点
             * 参数3:图像上边坐标点
             */
            canvas.drawBitmap(bitmap, (getWidth() - bitmap.getWidth())/2,getHeight()/2 - bitmap.getHeight() - 200, paint);
            canvas.save(); //保存当前画布的状态
            //参数 旋转的角度,旋转的 x 点坐标,旋转的 y 点坐标
            canvas.rotate(45,getWidth()/2 , getHeight()/2);
            canvas.drawBitmap(bitmap,(getWidth() - bitmap.getWidth())/2,(getHeight() - bitmap.getHeight())/2,paint); //在旋转后的画布上画图
            canvas.restore();//画布返回上一次的状态
            canvas.drawBitmap(bitmap,(getWidth() - bitmap.getWidth())/2,getHeight()/2  + 200,paint);
    
        }
    

    在这里插入图片描述

    (2) 我们先画一个图片–>保存画布—>旋转画布—>画一个图片----->不恢复画布---->再画一个图片

        protected void onDraw(Canvas canvas) {
    
            /**
             * 参数1:bitmap对象
             * 参数2:图像左边坐标点
             * 参数3:图像上边坐标点
             */
            canvas.drawBitmap(bitmap, (getWidth() - bitmap.getWidth())/2,getHeight()/2 - bitmap.getHeight() - 200, paint);
            canvas.save(); //保存当前画布的状态
            //参数 旋转的角度,旋转的 x 点坐标,旋转的 y 点坐标
            canvas.rotate(45,getWidth()/2 , getHeight()/2);
            canvas.drawBitmap(bitmap,(getWidth() - bitmap.getWidth())/2,(getHeight() - bitmap.getHeight())/2,paint); //在旋转后的画布上画图
          //  canvas.restore();//画布返回上一次的状态
            canvas.drawBitmap(bitmap,(getWidth() - bitmap.getWidth())/2,getHeight()/2  + 200,paint);
    
        }
    

    在这里插入图片描述

    我们也可以通过如下代码旋转更加直接体会 围绕中心旋转

        protected void onDraw(Canvas canvas) {
          canvas.drawLine(0,0,500,0,paint);
          paint.setColor(Color.BLUE);
          canvas.drawLine(0,0,0,500,paint);
    
          canvas.rotate(45,getWidth()/2,getHeight()/2); # 画布围绕中心旋转
    
            paint.setColor(Color.RED);
            canvas.drawLine(0,0,500,0,paint);
            paint.setColor(Color.BLUE);
            canvas.drawLine(0,0,0,500,paint);
        }
    

    在这里插入图片描述

    4、画布的平移

            paint = new Paint(); //新建一个画笔对象
            paint.setAntiAlias(true);//抗锯齿功能
            paint.setColor(Color.RED);  //设置画笔颜色
            paint.setStyle(Paint.Style.FILL);//设置填充样式
            paint.setStrokeWidth(30);//设置画笔宽度 ,单位px
    
            rect = new RectF(0,0,300,300);  //在  定点坐标系 新建一个矩形
    //省略········
        protected void onDraw(Canvas canvas) {
             canvas.drawRect(rect,paint);   //画第一个 矩形
             
             paint.setColor(Color.BLUE);  //设置画笔颜色
             canvas.translate(200,200);   //画布 在 x, y坐标系,平移  200,200
            canvas.drawRect(rect,paint); //画 第二个 矩形
    
            paint.setColor(Color.BLACK); //设置画笔颜色
            canvas.translate(200,200); //画布 在 x, y坐标系,平移  200,200 (默认是相对上一个原点的坐标)
            canvas.drawRect(rect,paint);//画第三个矩形
    
        }
    
    

    在这里插入图片描述

    5、画布的缩放

    1) canvas.scale(sx, sy);

            paint = new Paint(); //新建一个画笔对象
            paint.setAntiAlias(true);//抗锯齿功能
            paint.setColor(Color.RED);  //设置画笔颜色
            paint.setStyle(Paint.Style.FILL);//设置填充样式
            paint.setStrokeWidth(30);//设置画笔宽度 ,单位px
    
            rect = new RectF(0,0,600,600);  //定义一个 矩形 原点,大小 600 * 600
            //省略······
                protected void onDraw(Canvas canvas) {
             canvas.drawRect(rect,paint); //画 第一个 矩形
    
             // x,y轴所放的大小比例
             canvas.scale(0.5f,0.5f);   //缩小画布
             paint.setColor(Color.BLUE); /设置画笔颜色
             canvas.drawRect(rect,paint);//画第二个矩形
    
        }
    

    在这里插入图片描述

    1. canvas.scale(sx, sy, px,py)
      依照 原点 px,py 一点 进行缩放
      类似于先平移,在缩放
             canvas.scale(0.5f,0.5f,600,600);
    

    在这里插入图片描述

    展开全文
  • HTML5+Canvas实现的超炫粒子效果文字动画特效源码

    千次下载 热门讨论 2014-09-23 13:27:06
    这是一款基于HTML5 Canvas实现的文字特效源码,在页面下方的输入框中输入想要展示的文字,回车后即可在canvas上绘制出粒子效果的文字动画,动画效果炫酷十足,是值得收藏的html5经典特效。
  • Android Canvas绘图

    千次阅读 2018-10-31 17:52:36
    以图形处理来说,我们最常用到的就是在一个View上画一些图片、形状或者自定义的文本内容,这里我们都是使用Canvas来实现的。你可以获取View中的Canvas对象,绘制一些自定义形状,然后调用View. invalidate方法让View...

        以图形处理来说,我们最常用到的就是在一个View上画一些图片、形状或者自定义的文本内容,这里我们都是使用Canvas来实现的。你可以获取View中的Canvas对象,绘制一些自定义形状,然后调用View. invalidate方法让View重新刷新,然后绘制一个新的形状,这样达到2D动画效果。下面我们就主要来了解下Canvas的使用方法。

    Canvas对象的获取方式有两种:一种我们通过重写View.onDraw方法,View中的Canvas对象会被当做参数传递过来,我们操作这个Canvas,效果会直接反应在View中。另一种就是当你想创建一个Canvas对象时使用的方法:

    Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);

    Canvas c = new Canvas(b);

    上面代码创建了一个尺寸是100*100的Bitmap,使用它作为Canvas操作的对象,这时候的Canvas就是使用创建的方式。当你使用创建的Canvas在bitmap上执行绘制方法后,你还可以将绘制的结果提交给另外一个Canvas,这样就可以达到两个Canvas协作完成的效果,简化逻辑。但是android SDK建议使用View.onDraw参数里提供的Canvas就好,没必要自己创建一个新的Canvas对象。接下来我们看看Canvas提供我们哪些绘制图形的方法。我们创建一个自定义View对象,使用onDraw方法提供的Canvas进行绘制图形。

    CanvasDemoActivity.java:

    1. package com.android.demo.uicontroller.graphics;
    2.  
    3. import android.app.Activity;
    4. import android.content.Context;
    5. import android.graphics.Canvas;
    6. import android.graphics.Color;
    7. import android.graphics.Paint;
    8. import android.os.Bundle;
    9. import android.view.View;
    10.  
    11. public class CanvasDemoActivity extends Activity {
    12.  
    13. @Override
    14. protected void onCreate(Bundle savedInstanceState) {
    15. super.onCreate(savedInstanceState);
    16.  
    17. setContentView(new CustomView1(this));
    18.  
    19. }
    20.  
    21. /**
    22. * 使用内部类 自定义一个简单的View
    23. * @author Administrator
    24. *
    25. */
    26. class CustomView1 extends View{
    27.  
    28. Paint paint;
    29.  
    30. public CustomView1(Context context) {
    31. super(context);
    32. paint = new Paint(); //设置一个笔刷大小是3的黄色的画笔
    33. paint.setColor(Color.YELLOW);
    34. paint.setStrokeJoin(Paint.Join.ROUND);
    35. paint.setStrokeCap(Paint.Cap.ROUND);
    36. paint.setStrokeWidth(3);
    37. }
    38.  
    39. //在这里我们将测试canvas提供的绘制图形方法
    40. @Override
    41. protected void onDraw(Canvas canvas) {
    42.  
    43. }
    44.  
    45. }
    46.  
    47. }

    执行结果是一片黑色的区域,因为在自定义的CustomView中,我们没有做任何的绘制操作。canvas提供的绘制图形的方法都是以draw开头的,我们可以查看api:

    Css_bugtester

    从上面方法的名字看来我们可以知道Canvas可以绘制的对象有:

    弧线(arcs)、填充颜色(argb和color)、 Bitmap、圆(circle和oval)、点(point)、线(line)、矩形(Rect)、图片(Picture)、圆角矩形 (RoundRect)、文本(text)、顶点(Vertices)、路径(path)。通过组合这些对象我们可以画出一些简单有趣的界面出来,但是光有这些功能还是不够的,如果我要画一个仪表盘(数字围绕显示在一个圆圈中)呢? 幸好Android还提供了一些对Canvas位置转换的方法:rorate、scale、translate、skew(扭曲)等,而且它允许你通过获得它的转换矩阵对象(getMatrix方法,不知道什么是转换矩阵?看这里) 直接操作它。这些操作就像是虽然你的笔还是原来的地方画,但是画纸旋转或者移动了,所以你画的东西的方位就产生变化。为了方便一些转换操作,Canvas 还提供了保存和回滚属性的方法(save和restore),比如你可以先保存目前画纸的位置(save),然后旋转90度,向下移动100像素后画一些图形,画完后调用restore方法返回到刚才保存的位置。下面我们就演示下canvas的一些简单用法:

     
    1. protected void onDraw(Canvas canvas) {
    2.  
    3. canvas.drawCircle(100, 100, 90, paint);
    4. }

     

    效果是:

     
    1. @Override
    2. protected void onDraw(Canvas canvas) {
    3.  
    4. //绘制弧线区域
    5.  
    6. RectF rect = new RectF(0, 0, 100, 100);
    7.  
    8. canvas.drawArc(rect, //弧线所使用的矩形区域大小
    9. 0, //开始角度
    10. 90, //扫过的角度
    11. false, //是否使用中心
    12. paint);
    13.  
    14. }

    使用下面的代码:

     
    1. protected void onDraw(Canvas canvas) {
    2.  
    3. //绘制弧线区域
    4.  
    5. RectF rect = new RectF(0, 0, 100, 100);
    6.  
    7. canvas.drawArc(rect, //弧线所使用的矩形区域大小
    8. 0, //开始角度
    9. 90, //扫过的角度
    10. true, //是否使用中心
    11. paint);
    12.  
    13. }

     

    两图对比我们可以发现,当 drawArcs(rect,startAngel,sweepAngel,useCenter,paint)中的useCenter为false时,弧线区域是用弧线开始角度和结束角度直接连接起来的,当useCenter为true时,是弧线开始角度和结束角度都与中心点连接,形成一个扇形。

     
    1. protected void onDraw(Canvas canvas) {
    2.  
    3. canvas.drawColor(Color.BLUE);
    4.  
    5. }

    canvas.drawColor是直接将View显示区域用某个颜色填充满。

     
    1. @Override
    2. protected void onDraw(Canvas canvas) {
    3.  
    4. //画一条线
    5. canvas.drawLine(10, 10, 100, 100, paint);
    6.  
    7. }

     

    Canvas.drawOval:

     
    1. @Override
    2. protected void onDraw(Canvas canvas) {
    3.  
    4. //定义一个矩形区域
    5. RectF oval = new RectF(0,0,200,300);
    6. //矩形区域内切椭圆
    7. canvas.drawOval(oval, paint);
    8.  
    9. }

     

    canvas.drawPosText:

     
    1. @Override
    2. protected void onDraw(Canvas canvas) {
    3.  
    4. //按照既定点 绘制文本内容
    5. canvas.drawPosText("Android777", new float[]{
    6. 10,10, //第一个字母在坐标10,10
    7. 20,20, //第二个字母在坐标20,20
    8. 30,30, //....
    9. 40,40,
    10. 50,50,
    11. 60,60,
    12. 70,70,
    13. 80,80,
    14. 90,90,
    15. 100,100
    16. }, paint);
    17.  
    18. }

     

    canvas.drawRect:

     
    1. @Override
    2. protected void onDraw(Canvas canvas) {
    3.  
    4. RectF rect = new RectF(50, 50, 200, 200);
    5.  
    6. canvas.drawRect(rect, paint);
    7.  
    8. }
    9.  
    10. }

     

    canvas.drawRoundRect:

     
    1. @Override
    2. protected void onDraw(Canvas canvas) {
    3.  
    4. RectF rect = new RectF(50, 50, 200, 200);
    5.  
    6. canvas.drawRoundRect(rect,
    7. 30, //x轴的半径
    8. 30, //y轴的半径
    9. paint);
    10.  
    11. }

     

    canvas.drawPath:

     
    1. @Override
    2. protected void onDraw(Canvas canvas) {
    3.  
    4. Path path = new Path(); //定义一条路径
    5. path.moveTo(10, 10); //移动到 坐标10,10
    6. path.lineTo(50, 60);
    7. path.lineTo(200,80);
    8. path.lineTo(10, 10);
    9.  
    10. canvas.drawPath(path, paint);
    11.  
    12. }

    canvas.drawTextOnPath:

     
    1. @Override
    2. protected void onDraw(Canvas canvas) {
    3.  
    4. Path path = new Path(); //定义一条路径
    5. path.moveTo(10, 10); //移动到 坐标10,10
    6. path.lineTo(50, 60);
    7. path.lineTo(200,80);
    8. path.lineTo(10, 10);
    9.  
    10. // canvas.drawPath(path, paint);
    11. canvas.drawTextOnPath("Android777开发者博客", path, 10, 10, paint);
    12.  
    13. }

     

    位置转换方法,canvas.rorate和canvas.translate:

    1. @Override
    2. protected void onDraw(Canvas canvas) {
    3.  
    4. paint.setAntiAlias(true);
    5. paint.setStyle(Style.STROKE);
    6. canvas.translate(canvas.getWidth()/2, 200); //将位置移动画纸的坐标点:150,150
    7. canvas.drawCircle(0, 0, 100, paint); //画圆圈
    8.  
    9. //使用path绘制路径文字
    10. canvas.save();
    11. canvas.translate(-75, -75);
    12. Path path = new Path();
    13. path.addArc(new RectF(0,0,150,150), -180, 180);
    14. Paint citePaint = new Paint(paint);
    15. citePaint.setTextSize(14);
    16. citePaint.setStrokeWidth(1);
    17. canvas.drawTextOnPath("http://www.android777.com", path, 28, 0, citePaint);
    18. canvas.restore();
    19.  
    20. Paint tmpPaint = new Paint(paint); //小刻度画笔对象
    21. tmpPaint.setStrokeWidth(1);
    22.  
    23. float y=100;
    24. int count = 60; //总刻度数
    25.  
    26. for(int i=0 ; i <count ; i++){
    27. if(i%5 == 0){
    28. canvas.drawLine(0f, y, 0, y+12f, paint);
    29. canvas.drawText(String.valueOf(i/5+1), -4f, y+25f, tmpPaint);
    30.  
    31. }else{
    32. canvas.drawLine(0f, y, 0f, y +5f, tmpPaint);
    33. }
    34. canvas.rotate(360/count,0f,0f); //旋转画纸
    35. }
    36.  
    37. //绘制指针
    38. tmpPaint.setColor(Color.GRAY);
    39. tmpPaint.setStrokeWidth(4);
    40. canvas.drawCircle(0, 0, 7, tmpPaint);
    41. tmpPaint.setStyle(Style.FILL);
    42. tmpPaint.setColor(Color.YELLOW);
    43. canvas.drawCircle(0, 0, 5, tmpPaint);
    44. canvas.drawLine(0, 10, 0, -65, paint);
    45.  
    46. }

     

    上面几个例子基本已经将常用的canvas.draw*方法测试过了,我们结合一些事件,做一些有用户交互的应用:

     

    1. package com.android777.demo.uicontroller.graphics;
    2.  
    3. import java.util.ArrayList;
    4.  
    5. import android.app.Activity;
    6. import android.content.Context;
    7. import android.graphics.Canvas;
    8. import android.graphics.Color;
    9. import android.graphics.Paint;
    10. import android.graphics.PointF;
    11. import android.os.Bundle;
    12. import android.view.MotionEvent;
    13. import android.view.View;
    14.  
    15. public class CanvasDemoActivity extends Activity {
    16.  
    17. @Override
    18. protected void onCreate(Bundle savedInstanceState) {
    19. super.onCreate(savedInstanceState);
    20.  
    21. setContentView(new CustomView1(this));
    22.  
    23. }
    24.  
    25. /**
    26. * 使用内部类 自定义一个简单的View
    27. * @author Administrator
    28. *
    29. */
    30. class CustomView1 extends View{
    31.  
    32. Paint paint;
    33. private ArrayList<PointF> graphics = new ArrayList<PointF>();
    34. PointF point;
    35.  
    36. public CustomView1(Context context) {
    37. super(context);
    38. paint = new Paint(); //设置一个笔刷大小是3的黄色的画笔
    39. paint.setColor(Color.YELLOW);
    40. paint.setStrokeJoin(Paint.Join.ROUND);
    41. paint.setStrokeCap(Paint.Cap.ROUND);
    42. paint.setStrokeWidth(3);
    43.  
    44. }
    45.  
    46. @Override
    47. public boolean onTouchEvent(MotionEvent event) {
    48.  
    49. graphics.add(new PointF(event.getX(),event.getY()));
    50.  
    51. invalidate(); //重新绘制区域
    52.  
    53. return true;
    54. }
    55.  
    56. //在这里我们将测试canvas提供的绘制图形方法
    57. @Override
    58. protected void onDraw(Canvas canvas) {
    59. for (PointF point : graphics) {
    60. canvas.drawPoint(point.x, point.y, paint);
    61. }
    62. // super.onDraw(canvas);
    63.  
    64. }
    65. }
    66.  
    67. }

    当用户点击时将出现一个小点,拖动时将画出一条用细点组成的虚线:

     

    canvas的应用


    canva还可以制作很多自定义控件,比如google日历的monthview就是用canvas绘制出来的,github上有很多使用canva的项目,所有的图表库都是用canvas绘制的。

    展开全文
  • h5 Canvas矩形的绘制

    千次阅读 2019-02-26 16:05:43
    h5 Canvas矩形的绘制 1.绘制矩形api 在Canvas中提供了绘制矩形的API: fillRect(x, y, width, height):绘制一个填充的矩形 strokeRect(x, y, width, height):绘制一个矩形的边框 clearRect(x, y, width, height):...

    h5 Canvas矩形的绘制

    1.绘制矩形api

    在Canvas中提供了绘制矩形的API:

    fillRect(x, y, width, height):绘制一个填充的矩形
    strokeRect(x, y, width, height):绘制一个矩形的边框
    clearRect(x, y, width, height):清除指定矩形区域,让清除部分完全透明
    

    在这里插入图片描述
    除此之外还可以通过Canvas中CanvasRenderingContext2D.rect()路径方法创建矩形。这个方法需要配合CanvasRenderingContext2D.fill()绘制一个填充的矩形,CanvasRenderingContext2D.stroke()绘制一个填充的矩形。另外,还可以直接使用Canvas的绘制路径的方法来绘制矩形。其实绘制矩形的方法通过我们自己的思考,会有三种方式,分别是路径绘制矩形、rect()绘制矩形、fillRect()绘制填充矩形和strokeRect()绘制边框机矩形,那我们来先看看怎么使用路径绘制矩形。

    2.路径绘制矩形

    我们知道矩形是由4条边线围绕而成,因此我们只需要通过绘制4条头尾相连接的线段即可,我们通过学习之后就会发现绘制线段需要使用moveTo()和lineTo(),如此一来,四条线就能拼出一个矩形,然后通过fill()和stroke()绘制出填充和边框矩形。

    const canvas = document.getElementById('canvas');
    const context = canvas.getContext('2d');
    function drawScreen() {
      context.strokeStyle = '#00';
      context.fillStyle = '#e6c7ff';
      context.lineWidth = 2; // 设置线段宽度
      context.beginPath(); // 开始点
      context.moveTo(30, 30);
      context.lineTo(230, 30);
      context.lineTo(230, 200);
      context.lineTo(30, 200);
      context.lineTo(30, 30);
      context.stroke(); // 进行绘制外边框
    
      context.beginPath();
      context.moveTo(300, 30);
      context.lineTo(500, 30);
      context.lineTo(500, 200);
      context.lineTo(300, 200);
      context.lineTo(300, 30);
      context.fill(); // 进行内容填充
    }
    
    drawScreen();
    

    绘制结果:

    在Canvas中我们有一个closePath()的方法,在绘制矩形的时候,借助这个方法,我们通过绘制三条线段,就能和起始点闭合,也就绘制出相应的矩形。基于上面的示例,在stroke()和fill()前面添加closePath()即可:

    const canvas = document.getElementById('canvas');
    const context = canvas.getContext('2d');
    
    function drawScreen() {
      context.strokeStyle = '#00';
      context.fillStyle = '#e6c7ff';
      context.lineWidth = 2; // 设置线段宽度
      context.beginPath(); // 开始点
      context.moveTo(30, 30);
      context.lineTo(230, 30);
      context.lineTo(230, 200);
      context.lineTo(30, 200);
      context.closePath();
      context.stroke(); // 进行绘制外边框
    
      context.beginPath();
      context.moveTo(300, 30);
      context.lineTo(500, 30);
      context.lineTo(500, 200);
      context.lineTo(300, 200);
      context.closePath();
      context.fill(); // 进行内容填充
    }
    
    drawScreen();
    

    使用上面的绘制方法,同样能够达到绘制矩形的效果.

    3.rect()绘制矩形

    rect()也是Canvas中路径的一个方法,也需要配合fill()和stroke()。rect() 具有四个参数

    rect(x, y, width, height)
    

    其中x和y是矩形左上角的坐标点,width是矩形的宽度,height是矩形的高度。接下来,看如何使用rect()绘制矩形:

    const canvas = document.getElementById('canvas');
    const context = canvas.getContext('2d');
    function drawScreen () {
      context.strokeStyle = '#00';
      context.fillStyle = '#9f9';
      context.lineWidth = 2;
    
      context.beginPath();
      context.rect(30,30,200,200);
      context.stroke();
    
      context.beginPath();
      context.rect(300,30,200,200);
      context.fill();
    
    }
    
    drawScreen();
    

    显示效果如下:

    4.fillRect()绘制填充矩形和strokeRect()绘制边框矩形
    fillRect()绘制填充矩形

    前面两种方法是通过Canvas的路径方法绘制填充和边框矩形。那么在Canvas中可以直接通过fillRect()绘制一个矩形:

    fillRect(x,y,width,height)
    

    和rect()一样,x和y是矩形左上角的坐标点,width是矩形宽度,height是矩形高度:

    const canvas = document.getElementById('canvas');
    const context = canvas.getContext('2d');
    function drawScreen () {
      context.fillStyle = '#e0f'
      context.fillRect(30,30,200,200);
    }
    
    drawScreen();
    
    strokeRect()绘制边框矩形

    strokeRect()和fillRect()方法类似,只不过不同的是,strokeRect()绘制的是边框矩形:

    const canvas = document.getElementById('canvas');
    const context = canvas.getContext('2d');
    function drawScreen () {
      context.lineWidth = 4;
      context.strokeStyle = '#e0f'
      context.strokeRect(30,30,200,200);
    }
    
    drawScreen();
    
    清除矩形

    在Canvas中有一个clearRect()可以指定矩形区域内(以 点 (x, y) 为起点,范围是(width, height) )所有像素变成透明,并擦除之前绘制的所有内容的方法:

    context.clearRect(x, y, width, height);
    

    比如有时候需要清除画布,可以这样使用:

    context.save();
    context.clearRect(0, 0, canvas.width, canvas.height);
    
    5.同时绘制有边框和填充色的矩形

    前面我们看到的都是单独绘制边框或填充的矩形。那么将这两种结合在一起,我们就可以很容易的绘制出同时带有边框和填充色的矩形:

    const canvas = document.getElementById('canvas');
    const context = canvas.getContext('2d');
    function drawScreen () {
      context.lineWidth = 4;
      context.fillStyle = '#aa0';
      context.strokeStyle = '#9f9';
    
      // 连线方式
      context.beginPath();
      context.moveTo(10,10);
      context.lineTo(110,10);
      context.lineTo(110,110);
      context.lineTo(10,110);
      context.closePath();
      context.stroke();
      context.beginPath();
      context.moveTo(12,12);
      context.lineTo(108,12);
      context.lineTo(108,108);
      context.lineTo(12,108);
      context.closePath();
      context.fill();
    
      // rect方式
      context.beginPath();
      context.rect(120,10,100,100);
      context.closePath();
      context.stroke();
      context.beginPath();
      context.rect(122,12,96,96);
      context.fill();
    
      // strokeRect,fillRect方法
      context.strokeRect(240,10,100,100);
      context.fillRect(242,12,96,96);
    }
    
    drawScreen();
    
    6.绘制折角或圆角矩形

    如果我们要绘制一个折角的矩形或者圆角的矩形时,就需要借助lineJoin这个属性。不过有一点需要特别注意,lineJoin只适合于线段连接触端的样式控制。也就是说,他只适合边框矩形,如果没有边框的矩形是不生效的。话又说回来,如果需要一个填充的矩形需要有折角或圆角的效果时,就需要在填充矩形上加一个与填充色相同的边框。

    const canvas = document.getElementById('canvas');
    const context = canvas.getContext('2d');
    function drawScreen () {
      context.lineWidth = 10;
      context.strokeStyle = '#f99'
    
      context.lineJoin = "bevel";
      context.strokeRect(10,10,200,200);
    
      context.lineJoin = "round";
      context.strokeRect(250,10,200,200);
    }
    
    drawScreen();
    

    现在绘制成了已经有边角的矩形,如果我们需要绘制有填充颜色的图形时,只需要添加fillStyle属性即可

    不管是使用Canvas中的路径方法还是自带绘制矩形的API,都可以通过fillStyle和strokeStyle来给矩形设置样式,比如填充颜色和边框颜色。 ##### 7.通过JS手动绘制矩形 学习了以上基础知识,我们现在使用这些东西来实现使用鼠标实时绘制图形的功能,我们需要确认的是 >1.监听鼠标事件 2.鼠标按下事件 3.鼠标移动事件 4.鼠标抬起事件

    然后根据这些事件分别对应去实现canvas界面的修改
    1.添加监听事件

      canvas.addEventListener('mousedown', mouseDown, false);
      canvas.addEventListener('mouseup', mouseUp, false);
      canvas.addEventListener('mousemove', mouseMove, false);
    

    2.鼠标按下事件-mouseDown()函数
    mouseDown()函数是监听鼠标在画布上按下时需要做的事情:

    function mouseDown(e) {
        rect.startX = e.pageX - this.offsetLeft;
        rect.startY = e.pageY - this.offsetTop;
        drag = true;
    }
    

    当鼠标按下时,函数mouseDown()通过e.pageX和e.pageY找到e的位置,然后减去距离Canvas画布左边和顶部的距离。最后设置拖动drag为true。

    3.鼠标抬起事件-mouseUp()函数

    function mouseUp(){
        drag = false;
    }
    

    这个函数很简单,当用户释放鼠标时,拖动drag设回false。表示鼠标没有在拖动状态。

    4.鼠标移动事件-mouseMove()函数

    function mouseMove(e) {
        if (drag) {
            rect.w = (e.pageX - this.offsetLeft) - rect.startX; // 获取矩形的width
            rect.h = (e.pageY - this.offsetTop) - rect.startY ; // 获取矩形的height
            ctx.clearRect(0,0,myCanvas.width,myCanvas.height); // 清空canvas
            drawRect("fill"); // 调用绘制图形
        }
    }
    

    mouseMove()函数才是关键的一步。在mouseMove()函数中首先检测drag,如果是true意味着想要绘制一个矩形,如果是false只是意味着用户只在画布上移动鼠标,并不想画矩形。如果drag是true,通过鼠标跟随位置计算出矩形的宽度和高度。如果要做到这一点,我们需要减去鼠标当前的位置。这样得到想要绘制矩形的width和height,但在绘制之前,需要通过clearRect()方法,将Cavans画布清除干净。然后再调用绘制矩形的函数drawRect()。

    5.绘制矩形的方法-drawRect

    function drawRect(style){
        if (style==="fill"){ // 控制绘画填充的还是边框矩形
          ctx.fillRect(rect.startX, rect.startY, rect.w, rect.h);
        }
        if (style==="stroke"){
          ctx.strokeRect(rect.startX, rect.startY, rect.w, rect.h);
        }
    }
    

    drawRect()函数是真正绘制矩形的,在这个函数中传了一个style参数。如果是fill绘制一个填充矩形,如果是stroke将绘制一个边框矩形。

    height="600" width="100%" scrolling="no" title="h5Rect" src="//codepen.io/suwu150/embed/bZGOBj/?height=265&theme-id=0&default-tab=js,result" allowfullscreen="true"> See the Pen h5Rect by suwu150 ( @suwu150) on CodePen.
    总结

    本文介绍了在Canvas中绘制矩形的几种方法:

    1.使用Canvas中的路径moveTo()、lineTo()配合fill()和stroke()绘制矩形
    2.使用Canvas中的rect(x,y,width,height)配合fill()和stroke()绘制矩形
    3.使用fillRect(x,y,width,height)绘制一个填充的矩形
    4.使用strokeRect(x,y,width,height)绘制一个边框矩形
    5.另外还可以通过clearRect(x,y,width,height)清除画布。在Canvas中除了矩形之外,还有圆形之类的。


    展开全文
  • 基于HTML5 Canvas和jQuery 的画图工具的实现 参考博文: http://blog.csdn.net/luanlouis/article/details/38490589
  • Android Canvas基础详解

    万次阅读 多人点赞 2018-05-28 20:16:17
    Android中,如果我们想绘制复杂的自定义View或游戏,我们就需要熟悉绘图API。...Canvas是画布,我们通过Canvas的各种drawXXX方法将图形绘制到Canvas上面,在drawXXX方法中我们需要传入要绘制的图形...

    Android中,如果我们想绘制复杂的自定义View或游戏,我们就需要熟悉绘图API。Android通过Canvas类暴露了很多drawXXX方法,我们可以通过这些方法绘制各种各样的图形。Canvas绘图有三个基本要素:Canvas、绘图坐标系以及Paint。Canvas是画布,我们通过Canvas的各种drawXXX方法将图形绘制到Canvas上面,在drawXXX方法中我们需要传入要绘制的图形的坐标形状,还要传入一个画笔Paint。drawXXX方法以及传入其中的坐标决定了要绘制的图形的形状,比如drawCircle方法,用来绘制圆形,需要我们传入圆心的x和y坐标,以及圆的半径。drawXXX方法中传入的画笔Paint决定了绘制的图形的一些外观,比如是绘制的图形的颜色,再比如是绘制圆面还是圆的轮廓线等。Android系统的设计吸收了很多已有系统的诸多优秀之处,比如Canvas绘图。Canvas不是Android所特有的,Flex和Silverlight都支持Canvas绘图,Canvas也是HTML5标准中的一部分,主流的现代浏览器都支持用JavaScript在Canvas上绘图,如果你用过HTML5中的Canvas,你会发现Android的Canvas的绘图API与其很相似。总之,Canvas绘图不是Android所特有的。

    为了演示Android中各种drawXXX方法的时候,我做了一个App,通过单击相应的按钮绘制相应的图形,主界面如下所示: 
    这里写图片描述


    Canvas坐标系与绘图坐标系

    Canvas绘图中牵扯到两种坐标系:Canvas坐标系与绘图坐标系。

    • Canvas坐标系 
      Canvas坐标系指的是Canvas本身的坐标系,Canvas坐标系有且只有一个,且是唯一不变的,其坐标原点在View的左上角,从坐标原点向右为x轴的正半轴,从坐标原点向下为y轴的正半轴。

    • 绘图坐标系 
      Canvas的drawXXX方法中传入的各种坐标指的都是绘图坐标系中的坐标,而非Canvas坐标系中的坐标。默认情况下,绘图坐标系与Canvas坐标系完全重合,即初始状况下,绘图坐标系的坐标原点也在View的左上角,从原点向右为x轴正半轴,从原点向下为y轴正半轴。但不同于Canvas坐标系,绘图坐标系并不是一成不变的,可以通过调用Canvas的translate方法平移坐标系,可以通过Canvas的rotate方法旋转坐标系,还可以通过Canvas的scale方法缩放坐标系,而且需要注意的是,translate、rotate、scale的操作都是基于当前绘图坐标系的,而不是基于Canvas坐标系,一旦通过以上方法对坐标系进行了操作之后,当前绘图坐标系就变化了,以后绘图都是基于更新的绘图坐标系了。也就是说,真正对我们绘图有用的是绘图坐标系而非Canvas坐标系。

    为了更好的理解绘图坐标系,请看如下的代码:

        //绘制坐标系
        private void drawAxis(Canvas canvas){
            int canvasWidth = canvas.getWidth();
            int canvasHeight = canvas.getHeight();
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeCap(Paint.Cap.ROUND);
            paint.setStrokeWidth(6 * density);
    
            //用绿色画x轴,用蓝色画y轴
    
            //第一次绘制坐标轴
            paint.setColor(0xff00ff00);//绿色
            canvas.drawLine(0, 0, canvasWidth, 0, paint);//绘制x轴
            paint.setColor(0xff0000ff);//蓝色
            canvas.drawLine(0, 0, 0, canvasHeight, paint);//绘制y轴
    
            //对坐标系平移后,第二次绘制坐标轴
            canvas.translate(canvasWidth / 4, canvasWidth /4);//把坐标系向右下角平移
            paint.setColor(0xff00ff00);//绿色
            canvas.drawLine(0, 0, canvasWidth, 0, paint);//绘制x轴
            paint.setColor(0xff0000ff);//蓝色
            canvas.drawLine(0, 0, 0, canvasHeight, paint);//绘制y轴
    
            //再次平移坐标系并在此基础上旋转坐标系,第三次绘制坐标轴
            canvas.translate(canvasWidth / 4, canvasWidth / 4);//在上次平移的基础上再把坐标系向右下角平移
            canvas.rotate(30);//基于当前绘图坐标系的原点旋转坐标系
            paint.setColor(0xff00ff00);//绿色
            canvas.drawLine(0, 0, canvasWidth, 0, paint);//绘制x轴
            paint.setColor(0xff0000ff);//蓝色
            canvas.drawLine(0, 0, 0, canvasHeight, paint);//绘制y轴
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    界面如下所示: 
    这里写图片描述

    第一次绘制绘图坐标系时,绘图坐标系默认情况下和Canvas坐标系重合,所以绘制出的坐标系紧贴View的上侧和左侧; 
    第二次首先将坐标轴向右下角平移了一段距离,然后绘制出的坐标系也就整体向右下角平移了; 
    第三次再次向右下角平移,并旋转了30度,图上倾斜的坐标系即最后的绘图坐标系。


    drawARGB

    Canvas中的drawARGB可以用来对整个Canvas以某种统一的颜色整体绘制,四个参数分别是Alpha、Red、Green、Blue,取值都是0-255。 
    使用代码如下:

    private void drawARGB(Canvas canvas){
            canvas.drawARGB(255, 139, 197, 186);
        }
    • 1
    • 2
    • 3

    界面如下所示: 
    这里写图片描述


    drawText

    Canvas中用drawText方法绘制文字,代码如下所示:

    private void drawText(Canvas canvas){
            int canvasWidth = canvas.getWidth();
            int halfCanvasWidth = canvasWidth / 2;
            float translateY = textHeight;
    
            //绘制正常文本
            canvas.save();
            canvas.translate(0, translateY);
            canvas.drawText("正常绘制文本", 0, 0, paint);
            canvas.restore();
            translateY += textHeight * 2;
    
            //绘制绿色文本
            paint.setColor(0xff00ff00);//设置字体为绿色
            canvas.save();
            canvas.translate(0, translateY);//将画笔向下移动
            canvas.drawText("绘制绿色文本", 0, 0, paint);
            canvas.restore();
            paint.setColor(0xff000000);//重新设置为黑色
            translateY += textHeight * 2;
    
            //设置左对齐
            paint.setTextAlign(Paint.Align.LEFT);//设置左对齐
            canvas.save();
            canvas.translate(halfCanvasWidth, translateY);
            canvas.drawText("左对齐文本", 0, 0, paint);
            canvas.restore();
            translateY += textHeight * 2;
    
            //设置居中对齐
            paint.setTextAlign(Paint.Align.CENTER);//设置居中对齐
            canvas.save();
            canvas.translate(halfCanvasWidth, translateY);
            canvas.drawText("居中对齐文本", 0, 0, paint);
            canvas.restore();
            translateY += textHeight * 2;
    
            //设置右对齐
            paint.setTextAlign(Paint.Align.RIGHT);//设置右对齐
            canvas.save();
            canvas.translate(halfCanvasWidth, translateY);
            canvas.drawText("右对齐文本", 0, 0, paint);
            canvas.restore();
            paint.setTextAlign(Paint.Align.LEFT);//重新设置为左对齐
            translateY += textHeight * 2;
    
            //设置下划线
            paint.setUnderlineText(true);//设置具有下划线
            canvas.save();
            canvas.translate(0, translateY);
            canvas.drawText("下划线文本", 0, 0, paint);
            canvas.restore();
            paint.setUnderlineText(false);//重新设置为没有下划线
            translateY += textHeight * 2;
    
            //绘制加粗文字
            paint.setFakeBoldText(true);//将画笔设置为粗体
            canvas.save();
            canvas.translate(0, translateY);
            canvas.drawText("粗体文本", 0, 0, paint);
            canvas.restore();
            paint.setFakeBoldText(false);//重新将画笔设置为非粗体状态
            translateY += textHeight * 2;
    
            //文本绕绘制起点顺时针旋转
            canvas.save();
            canvas.translate(0, translateY);
            canvas.rotate(20);
            canvas.drawText("文本绕绘制起点旋转20度", 0, 0, paint);
            canvas.restore();
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    界面如下所示: 
    这里写图片描述

    对以上代码进行一下说明:

    1. Android中的画笔有两种Paint和TextPaint,我们可以Paint来画其他的图形:点、线、矩形、椭圆等。TextPaint继承自Paint,是专门用来画文本的,由于TextPaint继承自Paint,所以也可以用TextPaint画点、线、面、矩形、椭圆等图形。

    2. 我们在上面的代码中将canvas.translate()和canvas.rotate()放到了canvas.save()和canvas.restore()之间,这样做的好处是,在canvas.save()调用时,将当前坐标系保存下来,将当前坐标系的矩阵Matrix入栈保存,然后通过translate或rotate等对坐标系进行变换,然后进行绘图,绘图完成后,我们通过调用canvas.restore()将之前保存的Matrix出栈,这样就将当前绘图坐标系恢复到了canvas.save()执行的时候状态。如果熟悉OpenGL开发,对这种模式应该很了解。

    3. 通过调用paint.setColor(0xff00ff00)将画笔设置为绿色,paint的setColor方法需要传入一个int值,通常情况下我们写成16进制0x的形式,第一个字节存储Alpha通道,第二个字节存储Red通道,第三个字节存储Green通道,第四个字节存储Blue通道,每个字节的取值都是从00到ff。如果对这种设置颜色的方式不熟悉,也可以调用paint.setARGB(int a, int r, int g, int b)方法设置画笔的颜色,不过paint.setColor(int color)的方式更简洁。

    4. 通过调用paint.setTextAlign()设置文本的对齐方式,该对齐方式是相对于绘制文本时的画笔的坐标来说的,在本例中,我们绘制文本时画笔在Canvas宽度的中间。在drawText()方法执行时,需要传入一个x和y坐标,假设该点为P点,P点表示我们从P点绘制文本。当对齐方式为Paint.Align.LEFT时,绘制的文本以P点为基准向左对齐,这是默认的对齐方式;当对齐方式为Paint.Align.CENTER时,绘制的文本以P点为基准居中对齐;当对齐方式为Paint.Align.RIGHT时,绘制的文本以P点为基准向右对齐。

    5. 通过调用paint.setUnderlineText(true)绘制带有下划线的文本。

    6. 通过调用paint.setFakeBoldText(true)绘制粗体文本。

    7. 通过rotate旋转坐标系,我们可以绘制倾斜文本。


    drawPoint

    Canvas中用drawPoint方法绘制点,代码如下所示:

    private void drawPoint(Canvas canvas){
            int canvasWidth = canvas.getWidth();
            int canvasHeight = canvas.getHeight();
            int x = canvasWidth / 2;
            int deltaY = canvasHeight / 3;
            int y = deltaY / 2;
            paint.setColor(0xff8bc5ba);//设置颜色
            paint.setStrokeWidth(50 * density);//设置线宽,如果不设置线宽,无法绘制点
    
            //绘制Cap为BUTT的点
            paint.setStrokeCap(Paint.Cap.BUTT);
            canvas.drawPoint(x, y, paint);
    
            //绘制Cap为ROUND的点
            canvas.translate(0, deltaY);
            paint.setStrokeCap(Paint.Cap.ROUND);
            canvas.drawPoint(x, y, paint);
    
            //绘制Cap为SQUARE的点
            canvas.translate(0, deltaY);
            paint.setStrokeCap(Paint.Cap.SQUARE);
            canvas.drawPoint(x, y, paint);
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    界面如下所示: 
    这里写图片描述

    下面对以上代码进行说明:

    1. Paint的setStrokeWidth方法可以控制所画线的宽度,通过Paint的getStrokeWidth方法可以得到所画线的宽度,默认情况下,线宽是0。其实strokeWidth不仅对画线有影响,对画点也有影响,由于默认的线宽是0,所以默认情况下调用drawPoint方法无法在Canvas上画出点,为了让大家清楚地看到所画的点,我用Paint的setStrokeWidth设置了一个比较大的线宽,这样我们看到的点也就比较大。

    2. Paint有个setStrokeCap方法可以设置所画线段的时候两个端点的形状,即所画线段的帽端的形状,在下面讲到drawLine方法时会详细说明,其实setStrokeCap方法也会影响所画点的形状。Paint的setStrokeCap方法可以有三个取值:Paint.Cap.BUTT、Paint.Cap.ROUND和Paint.Cap.SQUARE。

    3. 默认情况下Paint的getStrokeCap的返回值是Paint.Cap.BUTT,默认画出来的点就是一个正方形,上图第一个点即是用BUTT作为帽端画的。

    4. 我们可以调用setStrokeCap方法设置Paint的strokeCap为Paint.Cap.ROUND时,画笔画出来的点就是一个圆形,上图第二个点即是用ROUND作为帽端画的。

    5. 调用调用setStrokeCap方法设置Paint的strokeCap为Paint.Cap.SQUARE时,画笔画出来的电也是一个正方形,与用BUTT画出来的效果在外观上相同,上图最后一个点即时用SQUARE作为帽端画的。


    drawLine

    Canvas通过drawLine方法绘制一条线段,通过drawLines方法绘制多段线,使用代码如下所示: 
    这里写图片描述

    下面对以上代码进行说明:

    1. drawLine方法接收四个数值,即起点的x和y以及终点的x和y,绘制一条线段。

    2. drawLines方法接收一个float数组pts,需要注意的是在用drawLines绘图时,其每次从pts数组中取出四个点绘制一条线段,然后再取出后面四个点绘制一条线段,所以要求pts的长度需要是4的倍数。假设我们有四个点,分别是p1、p2、p3、p4,我们依次将其坐标放到pts数组中,即pts = {p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y},那么用drawLines绘制pts时,你会发现p1和p2之间画了一条线段,p3和p4之间画了一条线段,但是p2和p3之间没有画线段,这样大家就应该能明白drawLines每次都需要从pts数组中取出4个值绘制一条线段的意思了。

    3. 通过调用Paint的setStrokeWidth方法设置线的宽度。

    4. 上面在讲drawPoint时提到了strokeCap对所绘制点的形状的影响,通过drawLine绘制的线段也受其影响,体现在绘制的线段的两个端点的形状上。

      • Paint.Cap.BUTT 
        当用BUTT作为帽端时,所绘制的线段恰好在起点终点位置处戛然而止,两端是方形,上图中第一条加粗的线段就是用BUTT作为帽端绘制的。

      • Paint.Cap.ROUND 
        当用ROUND作为帽端时,所绘制的线段的两端端点会超出起点和终点一点距离,并且两端是圆形状,上图中第二条加粗的线段就是用ROUND作为帽端绘制的。

      • Paint.Cap.SQUARE 
        当用SQUARE作为帽端时,所绘制的线段的两端端点也会超出起点和终点一点距离,两端点的形状是方形,上图中最后一条加粗的线段就是用SQUARE作为帽端绘制的。


    drawRect

    Canvas通过drawRect方法绘制矩形,使用代码如下所示:

    private void drawRect(Canvas canvas){
            int canvasWidth = canvas.getWidth();
            int canvasHeight = canvas.getHeight();
    
            //默认画笔的填充色是黑色
            int left1 = 10;
            int top1 = 10;
            int right1 = canvasWidth / 3;
            int bottom1 = canvasHeight /3;
            canvas.drawRect(left1, top1, right1, bottom1, paint);
    
            //修改画笔颜色
            paint.setColor(0xff8bc5ba);//A:ff,R:8b,G:c5,B:ba
            int left2 = canvasWidth / 3 * 2;
            int top2 = 10;
            int right2 = canvasWidth - 10;
            int bottom2 = canvasHeight / 3;
            canvas.drawRect(left2, top2, right2, bottom2, paint);
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    界面如下所示: 
    这里写图片描述

    其方法签名是drawRect(float left, float top, float right, float bottom, Paint paint),left和right表示矩形的左边和右边分别到绘图坐标系y轴正半轴的距离,top和bottom表示矩形的上边和下边分别到绘图坐标系x轴正半轴的距离。


    drawCircle

    Canvas中用drawCircle方法绘制圆形,使用代码如下所示:

    private void drawCircle(Canvas canvas){
            paint.setColor(0xff8bc5ba);//设置颜色
            paint.setStyle(Paint.Style.FILL);//默认绘图为填充模式
            int canvasWidth = canvas.getWidth();
            int canvasHeight = canvas.getHeight();
            int halfCanvasWidth = canvasWidth / 2;
            int count = 3;
            int D = canvasHeight / (count + 1);
            int R = D / 2;
    
            //绘制圆
            canvas.translate(0, D / (count + 1));
            canvas.drawCircle(halfCanvasWidth, R, R, paint);
    
            //通过绘制两个圆形成圆环
            //1. 首先绘制大圆
            canvas.translate(0, D + D / (count + 1));
            canvas.drawCircle(halfCanvasWidth, R, R, paint);
            //2. 然后绘制小圆,让小圆覆盖大圆,形成圆环效果
            int r = (int)(R * 0.75);
            paint.setColor(0xffffffff);//将画笔设置为白色,画小圆
            canvas.drawCircle(halfCanvasWidth, R, r, paint);
    
            //通过线条绘图模式绘制圆环
            canvas.translate(0, D + D / (count + 1));
            paint.setColor(0xff8bc5ba);//设置颜色
            paint.setStyle(Paint.Style.STROKE);//绘图为线条模式
            float strokeWidth = (float)(R * 0.25);
            paint.setStrokeWidth(strokeWidth);
            canvas.drawCircle(halfCanvasWidth, R, R, paint);
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    界面如下所示: 
    这里写图片描述

    下面对以上代码进行说明:

    1. 其方法签名是drawCircle (float cx, float cy, float radius, Paint paint),在使用时需要传入圆心的坐标以及半径,当然还有画笔Paint对象。

    2. 当我们在调用drawCircle、drawOval、drawArc、drawRect等方法时,我们既可以绘制对应图形的填充面,也可以只绘制该图形的轮廓线,控制的关键在于画笔Paint中的style。Paint通过setStyle方法设置要绘制的类型,style有取三种值:Paint.Style.FILL、Paint.Style.STROKE和Paint.Style.FILL_AND_STROKE。

      • 当style为FILL时,绘制是填充面,FILL是Paint默认的style;
      • 当style为STROKE时,绘制的是图形的轮廓线;
      • 当style为FILL_AND_STROKE时,同时绘制填充面和轮廓线,不过这种情况用的不多,因为填充面和轮廓线是用同一种颜色绘制的,区分不出轮廓线的效果。
    3. 在Paint的style是FILL时,我们通过drawCircle绘制出圆面,如上图中的第一个图形所示。

    4. 我们可以通过绘制两个圆面的方式绘制出圆环的效果。首先将画笔设置为某一颜色,且style设置为FILL状态,通过drawCircle绘制一个大的圆面;然后将画笔Paint的颜色改为白色或其他颜色,并减小半径再次通过drawCircle绘制一个小圆,这样就用小圆遮盖了大圆的一部分,未遮盖的部分便自然形成了圆环的效果,如上图中的第二个图形所示。

    5. 除了上述方法,我们还有一种办法绘制圆环的效果。我们首先将画笔Paint的style设置为STROKE模式,表示画笔处于画线条模式,而非填充模式。然后为了让圆环比较明显有一定的宽度,我们需要调用Paint的setStrokeWidth方法设置线宽。最后调用drawCircle方法绘制出宽度比较大的圆的轮廓线,也就形成了圆环效果,如上图中的最后一个图形所示。此处需要说明的是,当我们用STROKE模式画圆时,轮廓线是以实际圆的边界为分界线分别向内向外扩充1/2的线宽的距离,比如圆的半径是100,线宽是20,那么在STROKE模式下绘制出的圆环效果相当于半径为110的大圆和半径为90的小圆形成的效果,100 + 20 / 2 = 110, 100 - 20/2 = 90。


    drawOval

    Canvas中提供了drawOval方法绘制椭圆,其使用代码如下所示:

    private void drawOval(Canvas canvas){
            int canvasWidth = canvas.getWidth();
            int canvasHeight = canvas.getHeight();
            float quarter = canvasHeight / 4;
            float left = 10 * density;
            float top = 0;
            float right = canvasWidth - left;
            float bottom= quarter;
            RectF rectF = new RectF(left, top, right, bottom);
    
            //绘制椭圆形轮廓线
            paint.setStyle(Paint.Style.STROKE);//设置画笔为画线条模式
            paint.setStrokeWidth(2 * density);//设置线宽
            paint.setColor(0xff8bc5ba);//设置线条颜色
            canvas.translate(0, quarter / 4);
            canvas.drawOval(rectF, paint);
    
            //绘制椭圆形填充面
            paint.setStyle(Paint.Style.FILL);//设置画笔为填充模式
            canvas.translate(0, (quarter + quarter / 4));
            canvas.drawOval(rectF, paint);
    
            //画两个椭圆,形成轮廓线和填充色不同的效果
            canvas.translate(0, (quarter + quarter / 4));
            //1. 首先绘制填充色
            paint.setStyle(Paint.Style.FILL);//设置画笔为填充模式
            canvas.drawOval(rectF, paint);//绘制椭圆形的填充效果
            //2. 将线条颜色设置为蓝色,绘制轮廓线
            paint.setStyle(Paint.Style.STROKE);//设置画笔为线条模式
            paint.setColor(0xff0000ff);//设置填充色为蓝色
            canvas.drawOval(rectF, paint);//设置椭圆的轮廓线
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    其界面如下所示: 
    这里写图片描述

    下面对以上代码进行说明:

    1. 其方法签名是public void drawOval (RectF oval, Paint paint),RectF有四个字段,分别是left、top、right、bottom, 
      这四个值对应了椭圆的左、上、右、下四个点到相应坐标轴的距离,具体来说,left和right表示椭圆的最左侧的点和最右侧的点到绘图坐标系的y轴的距离,top和bottom表示椭圆的最顶部的点和最底部的点到绘图坐标系的x轴的距离,这四个值就决定了椭圆的形状,right与left的差值即为椭圆的长轴,bottom与top的差值即为椭圆的短轴,如下图所示: 
      这里写图片描述

    2. 通过Paint的setStyle方法将画笔的style设置成STROKE,即画线条模式,这种情况下,用画笔画出来的是椭圆的轮廓线,而非填充面,如上图中的第一个图形所示。

    3. 当将画笔Paint的style设置为FILL时,即填充模式,这种情况下,用画笔画出来的是椭圆的填充面,如上图中的第二个图形所示。

    4. 如果我们想绘制带有其他颜色轮廓线的椭圆面,我们需要绘制两个椭圆。首先以FILL模式画一个椭圆的填充面,然后更改画笔颜色,以STROKE模式画椭圆的轮廓线,如上图中的最后一个图形所示。这样从外观上看,好像是椭圆面与椭圆的轮廓线颜色不同。


    drawArc

    Canvas中提供了drawArc方法用于绘制弧,这里的弧指两种:弧面和弧线,弧面即用弧围成的填充面,弧线即为弧面的轮廓线。其使用代码如下所示:

    private void drawArc(Canvas canvas){
            int canvasWidth = canvas.getWidth();
            int canvasHeight = canvas.getHeight();
            int count = 5;
            float ovalHeight = canvasHeight / (count + 1);
            float left = 10 * density;
            float top = 0;
            float right = canvasWidth - left;
            float bottom= ovalHeight;
            RectF rectF = new RectF(left, top, right, bottom);
    
            paint.setStrokeWidth(2 * density);//设置线宽
            paint.setColor(0xff8bc5ba);//设置颜色
            paint.setStyle(Paint.Style.FILL);//默认设置画笔为填充模式
    
            //绘制用drawArc绘制完整的椭圆
            canvas.translate(0, ovalHeight / count);
            canvas.drawArc(rectF, 0, 360, true, paint);
    
            //绘制椭圆的四分之一,起点是钟表的3点位置,从3点绘制到6点的位置
            canvas.translate(0, (ovalHeight + ovalHeight / count));
            canvas.drawArc(rectF, 0, 90, true, paint);
    
            //绘制椭圆的四分之一,将useCenter设置为false
            canvas.translate(0, (ovalHeight + ovalHeight / count));
            canvas.drawArc(rectF, 0, 90, false, paint);
    
            //绘制椭圆的四分之一,只绘制轮廓线
            paint.setStyle(Paint.Style.STROKE);//设置画笔为线条模式
            canvas.translate(0, (ovalHeight + ovalHeight / count));
            canvas.drawArc(rectF, 0, 90, true, paint);
    
            //绘制带有轮廓线的椭圆的四分之一
            //1. 先绘制椭圆的填充部分
            paint.setStyle(Paint.Style.FILL);//设置画笔为填充模式
            canvas.translate(0, (ovalHeight + ovalHeight / count));
            canvas.drawArc(rectF, 0, 90, true, paint);
            //2. 再绘制椭圆的轮廓线部分
            paint.setStyle(Paint.Style.STROKE);//设置画笔为线条模式
            paint.setColor(0xff0000ff);//设置轮廓线条为蓝色
            canvas.drawArc(rectF, 0, 90, true, paint);
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    界面如下所示: 
    这里写图片描述

    下面对以上代码进行说明:

    1. 用drawArc画的弧指的是椭圆弧,即椭圆的一部分。当然,如果椭圆的长轴和和短轴相等,这时候我们就可以用drawArc方法绘制圆弧。其方法签名是:

      public void drawArc (RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

      • oval是RecF类型的对象,其定义了椭圆的形状。
      • startAngle指的是绘制的起始角度,钟表的3点位置对应着0度,如果传入的startAngle小于0或者大于等于360,那么用startAngle对360进行取模后作为起始绘制角度。
      • sweepAngle指的是从startAngle开始沿着钟表的顺时针方向旋转扫过的角度。如果sweepAngle大于等于360,那么会绘制完整的椭圆弧。如果sweepAngle小于0,那么会用sweepAngle对360进行取模后作为扫过的角度。
      • useCenter是个boolean值,如果为true,表示在绘制完弧之后,用椭圆的中心点连接弧上的起点和终点以闭合弧;如果值为false,表示在绘制完弧之后,弧的起点和终点直接连接,不经过椭圆的中心点。
    2. 在代码中我们一开始设置的Paint的style为FILL,即填充模式。通过上面的描述我们知道,drawOval方法可以看做是drawArc方法的一种特例。如果在drawArc方法中sweepAngle为360,无论startAngle为多少,drawArc都会绘制一个椭圆,如上图中第一个图形,我们用canvas.drawArc(rectF, 0, 360, true, paint)绘制了一个完整的椭圆,就像用drawOval画出的那样。

    3. 当我们调用方法canvas.drawArc(rectF, 0, 90, true, paint)时, 我们指定了起始角度为0,然后顺时针绘制90度,即我们会绘制从3点到6点这90度的弧,如上图中第二个图形所示,我们绘制了一个椭圆的右下角的四分之一的弧面,需要注意的是我们此处设置的useCenter为true,所以弧上的起点(3点位置)和终点(6点位置)都和椭圆的中心连接了形成了。

    4. 当我们调用方法canvas.drawArc(rectF, 0, 90, false, paint)时,我们还是绘制椭圆右下角的弧面,不过这次我们将useCenter设置成了false,如上图中的第三个图形所示,弧上的起点(3点位置)和终点(6点位置)直接相连闭合了,而没有经过椭圆的中心点。

    5. 上面介绍到的绘图都是在画笔Paint处于FILL状态下绘制的。我们可以通过paint.setStyle(Paint.Style.STROKE)方法将画笔的style改为STROKE,即绘制线条模式。然后我们再次执行canvas.drawArc(rectF, 0, 90, true, paint),初始角度为0,扫过90度的区域,useCenter为true,绘制的效果见上图中第四个图形,此时我们只绘制了椭圆的轮廓线。需要注意的,由于Paint默认的线宽为0,所以在绘制之前要确保掉用过Paint.setStrokeWidth()方法以设置画笔的线宽。

    6. 如果我们想绘制出带有其他颜色轮廓线的弧面时,该怎么办呢?我们可以分两步完成:首先,将画笔Paint的style设置为FILL模式,通过drawArc方法绘制出弧面。然后,将画笔Paint的style设置为STROKE模式,并通过paint的setColor()方法改变画笔的颜色,最后drawArc方法绘制出弧线。这样我们就能绘制出带有其他颜色轮廓线的弧面了,如上图中最后一个图形所示。


    drawPath

    Canvas通过drawPath方法可以绘制Path。那Path是什么呢?Path致以过来是路径的意思,在Android中,Path是一种线条的组合图形,其可以由直线、二次曲线、三次曲线、椭圆的弧等组成。Path既可以画线条,也可以画填充面。其使用代码如下所示:

    private void drawPath(Canvas canvas){
            int canvasWidth = canvas.getWidth();
            int deltaX = canvasWidth / 4;
            int deltaY = (int)(deltaX * 0.75);
    
            paint.setColor(0xff8bc5ba);//设置画笔颜色
            paint.setStrokeWidth(4);//设置线宽
    
            /*--------------------------用Path画填充面-----------------------------*/
            paint.setStyle(Paint.Style.FILL);//设置画笔为填充模式
            Path path = new Path();
            //向Path中加入Arc
            RectF arcRecF = new RectF(0, 0, deltaX, deltaY);
            path.addArc(arcRecF, 0, 135);
            //向Path中加入Oval
            RectF ovalRecF = new RectF(deltaX, 0, deltaX * 2, deltaY);
            path.addOval(ovalRecF, Path.Direction.CCW);
            //向Path中添加Circle
            path.addCircle((float)(deltaX * 2.5), deltaY / 2, deltaY / 2, Path.Direction.CCW);
            //向Path中添加Rect
            RectF rectF = new RectF(deltaX * 3, 0, deltaX * 4, deltaY);
            path.addRect(rectF, Path.Direction.CCW);
            canvas.drawPath(path, paint);
    
            /*--------------------------用Path画线--------------------------------*/
            paint.setStyle(Paint.Style.STROKE);//设置画笔为线条模式
            canvas.translate(0, deltaY * 2);
            Path path2 = path;
            canvas.drawPath(path2, paint);
    
            /*-----------------使用lineTo、arcTo、quadTo、cubicTo画线--------------*/
            paint.setStyle(Paint.Style.STROKE);//设置画笔为线条模式
            canvas.translate(0, deltaY * 2);
            Path path3 = new Path();
            //用pointList记录不同的path的各处的连接点
            List<Point> pointList = new ArrayList<Point>();
            //1. 第一部分,绘制线段
            path3.moveTo(0, 0);
            path3.lineTo(deltaX / 2, 0);//绘制线段
            pointList.add(new Point(0, 0));
            pointList.add(new Point(deltaX / 2, 0));
            //2. 第二部分,绘制椭圆右上角的四分之一的弧线
            RectF arcRecF1 = new RectF(0, 0, deltaX, deltaY);
            path3.arcTo(arcRecF1, 270, 90);//绘制圆弧
            pointList.add(new Point(deltaX, deltaY / 2));
            //3. 第三部分,绘制椭圆左下角的四分之一的弧线
            //注意,我们此处调用了path的moveTo方法,将画笔的移动到我们下一处要绘制arc的起点上
            path3.moveTo(deltaX * 1.5f, deltaY);
            RectF arcRecF2 = new RectF(deltaX, 0, deltaX * 2, deltaY);
            path3.arcTo(arcRecF2, 90, 90);//绘制圆弧
            pointList.add(new Point((int)(deltaX * 1.5), deltaY));
            //4. 第四部分,绘制二阶贝塞尔曲线
            //二阶贝塞尔曲线的起点就是当前画笔的位置,然后需要添加一个控制点,以及一个终点
            //再次通过调用path的moveTo方法,移动画笔
            path3.moveTo(deltaX * 1.5f, deltaY);
            //绘制二阶贝塞尔曲线
            path3.quadTo(deltaX * 2, 0, deltaX * 2.5f, deltaY / 2);
            pointList.add(new Point((int)(deltaX * 2.5), deltaY / 2));
            //5. 第五部分,绘制三阶贝塞尔曲线,三阶贝塞尔曲线的起点也是当前画笔的位置
            //其需要两个控制点,即比二阶贝赛尔曲线多一个控制点,最后也需要一个终点
            //再次通过调用path的moveTo方法,移动画笔
            path3.moveTo(deltaX * 2.5f, deltaY / 2);
            //绘制三阶贝塞尔曲线
            path3.cubicTo(deltaX * 3, 0, deltaX * 3.5f, 0, deltaX * 4, deltaY);
            pointList.add(new Point(deltaX * 4, deltaY));
    
            //Path准备就绪后,真正将Path绘制到Canvas上
            canvas.drawPath(path3, paint);
    
            //最后绘制Path的连接点,方便我们大家对比观察
            paint.setStrokeWidth(10);//将点的strokeWidth要设置的比画path时要大
            paint.setStrokeCap(Paint.Cap.ROUND);//将点设置为圆点状
            paint.setColor(0xff0000ff);//设置圆点为蓝色
            for(Point p : pointList){
                //遍历pointList,绘制连接点
                canvas.drawPoint(p.x, p.y, paint);
            }
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    界面如下所示: 
    这里写图片描述

    下面对以上代码进行说明:

    1. Canvas的drawPath()方法接收Path和Paint两个参数。当Paint的style是FILL时,我们可以用darwPath来画填充面。Path类提供了addArc、addOval、addCircle、addRect等方法,可以通过这些方法可以向Path添加各种闭合图形,Path甚至还提供了addPath方法让我们将一个Path对象添加到另一个Path对象中作为其一部分。当我们通过Path的addXXX方法向Path中添加了各种图形后,我们就可以调用canvas.drawPath(path, paint)绘制出Path了,如上图中第一行中的几个图形所示。

    2. 我们可以通过调用Paint的setStyle()方法将画笔Paint设置为STROKE,即线条模式, 然后我们再次执行canvas.darwPath()方法绘制同一个Path对象,我们这次绘制的就只是Path的轮廓线了,如上图中第二行中的几个图形所示。

    3. Path对象还有很多xxTo方法,比如lineTo、arcTo、quadTo、cubicTo等,通过这些方法,我们可以方便的从画笔位置绘制到指定坐标的连续线条,如上图中最后一行的几个线状图形所示。我们用了lineTo、arcTo、quadTo、cubicTo这四种方法画了五段线条,下面会解释,并且单独通过调用drawPoint画出了每段线条的两个端点,方便大家观察。

      • moveTo方法用于设置下一个线条的起始点,可以认为是移动了画笔,但说移动画笔不严格,后面会解释,此处大家暂且这么理解。

      • lineTo的方法签名是public void lineTo (float x, float y),Path的lineTo方法会从当前画笔的位置到我们指定的坐标构建一条线段,然后将其添加到Path对象中,如上图中最后一行图形中的第一条线段所示。

      • arcTo的方法签名是public void arcTo (RectF oval, float startAngle, float sweepAngle),oval、startAngle与sweepAngle的参数与之前提到的darwArc方法对应的形参意义相同,在此不再赘述。Path的arcTo方法会构建一条弧线并添加到Path对象中,如上图中最后一行图形中的第二条和第三条线状图形所示,这两条弧线都是通过Path的arcTo方法添加的。

      • quadTo是用来画二阶贝塞尔曲线的,即抛物线,其方法签名是public void quadTo (float x1, float y1, float x2, float y2),如果对贝塞尔曲线的相关概念不了解,推荐大家读一下博文《贝塞尔曲线初探》 。下面借用该博文中的一张图说一下二阶贝塞尔曲线: 
        这里写图片描述 
        二阶贝塞尔曲线的绘制一共需要三个点,一个起点,一个终点,还要有一个中间的控制点。我们画笔的位置就相当于上图中P0的位置,quadTo中的前两个参数x1和y1指定了控制点P1的坐标,后面两个参数x2和y2指定了终点P2的坐标。上图中最后一行的第四个线状图形就是用quadTo绘制的二阶贝塞尔曲线。

      • cubicTo跟quadTo类似,不过是用来画三阶贝塞尔曲线的,其方法签名是public void cubicTo (float x1, float y1, float x2, float y2, float x3, float y3)。我们还是借用一下上述博文《贝塞尔曲线初探》中的另一张图片来解释一下三阶贝塞尔曲线: 
        这里写图片描述 
        三阶贝塞尔曲线的绘制需要四个点,一个起点,一个终点,以及两个中间的控制点,也就是说它比二阶贝塞尔曲线要多一个控制点。我们画笔的位置就相当于上图中P0的位置,cubicTo中的前两个参数x1和y1指定了第一个控制点P1的坐标,参数x2和y2指定了第二个控制点P2的坐标,最后两个参数x3和y3指定了终点P3的坐标。上图中最后一行的最后一个线状图形就是用cubicTo绘制的三阶贝塞尔曲线。

    4. 上面提到Path的moveTo方法移动了画笔的位置,这样说不准确,因为Path和Paint没有任何关系,准确的说法是移动了Path的当前点,当我们调用lineTo、arcTo、quadTo、cubicTo等方法时,首先要从当前点开始绘制。对于lineTo、quadTo、cubicTo这三个方法来说,Path的当前点作为了这三个方法绘制的线条中的起始点,但是对于arcTo方法来说却不同。当我们调用arcTo方法时,首先会从Path的当前点画一条直线到我们所画弧的起始点,所以在使用Path的arcTo方法前要注意通过调用Path的moveTo方法使当前点与所画弧的起点重合,否则有可能你就会看到多了一条当前点到弧的起点的线段。moveTo可以移动当前点,当调用了lineTo、arcTo、quadTo、cubicTo等方法时,当前点也会移动,当前点就变成了所绘制的线条的最后一个点。

    5. 上面提到了moveTo、lineTo、arcTo、quadTo、cubicTo的方法中传入的坐标都是绘图坐标系中的坐标,即绘图坐标系中的绝对坐标。其实我们可以用相对坐标调用这些类型功能的方法。Path因此提供了对应的rMoveTo、rLineTo、rQuadTo、rCubicTo方法,其形参列表与对应的方法相同,只不过里面传入的坐标不是相对于当前点的相对坐标,即传入的坐标是相对于当前点的偏移值。

    6. lineTo、arcTo、quadTo、cubicTo等方法只是向Path中添加相应的线条,只有执行了canvas.drawPath(path3, paint)方法时,我们才能将Path绘制到Canvas上。


    drawBitmap

    Canvas中提供了drawBitmap方法用于绘制Bitmap,其使用代码如下所示:

    private void drawBitmap(Canvas canvas){
            //如果bitmap不存在,那么就不执行下面的绘制代码
            if(bitmap == null){
                return;
            }
    
            //直接完全绘制Bitmap
            canvas.drawBitmap(bitmap, 0, 0, paint);
    
            //绘制Bitmap的一部分,并对其拉伸
            //srcRect定义了要绘制Bitmap的哪一部分
            Rect srcRect = new Rect();
            srcRect.left = 0;
            srcRect.right = bitmap.getWidth();
            srcRect.top = 0;
            srcRect.bottom = (int)(0.33 * bitmap.getHeight());
            float radio = (float)(srcRect.bottom - srcRect.top)  / bitmap.getWidth();
            //dstRecF定义了要将绘制的Bitmap拉伸到哪里
            RectF dstRecF = new RectF();
            dstRecF.left = 0;
            dstRecF.right = canvas.getWidth();
            dstRecF.top = bitmap.getHeight();
            float dstHeight = (dstRecF.right - dstRecF.left) * radio;
            dstRecF.bottom = dstRecF.top + dstHeight;
            canvas.drawBitmap(bitmap, srcRect, dstRecF, paint);
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    界面如下所示: 
    这里写图片描述

    我在res/drawable目录下放置了一张android的图片,下面对上面的代码进行说明:

    1. Canvas的drawBitmap有多个重载方法,最简单的方法签名是:

      public void drawBitmap (Bitmap bitmap, float left, float top, Paint paint)

      该方法除了传入bitmap对象外,还需要传入left和top,left和top组成了一个坐标,决定了在Canvas中从哪个地方绘制Bitmap。在我们的代码中,left和top都设置为0,所以我们就在Canvas的左上角绘制了bitmap。

    2. drawBitmap还有一个比较实用的方法,其方法签名是:

      public void drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint paint)

      该方法有两个功能:1.只绘制原有bitmap对象的一部分,2.还可以将要绘制的bitmap缩放到指定的区域。

      • 只绘制原有bitmap对象的一部分 
        我们知道Bitmap是一个矩形,其是有宽度和高度的,也就说以bitmap对象本身作为坐标系(原点在bitmap左上角),我们可以构建一个Rect对象,如果满足left为0,top为0,right为bitmap的宽度,bottom为bitmap的高度,那么就说名我们要绘制整个Bitmap。但是有时候我们只想绘制Bitmap的一部分,例如我们上面的图中所示,我们想只绘制Android图像的头部区域怎么办呢?办法是我们构建一个Rect对象,定义我们要绘制Bitmap的哪些部位。 
        比如我们通过代码srcRect.bottom = (int)(0.33 * bitmap.getHeight())指定了我们只绘制bitmap对象头部1/3的位置,即Android图像的头部,这样我们用该指定的srcRect绘制bitmap时只绘制了其头部位置。需要特别注意的是,srcRect中left、top、right、bottom的值都是以Bitmap本身的局部坐标系为基础的。

      • 将要绘制的bitmap缩放到指定的区域 
        有时候我们需要将原有的bitmap进行放大或缩小,如上图所示,我们将原有图片放大了,这怎么做呢?我们需要指定RectF类型的参数dstRectF,以便告诉Android将srcRect中定义的bitmap缩放到哪里。即Android会将srcRect中定义的bitmap缩放到dstRectF区域范围内。需要注意的是,此处的dstRecF是绘图坐标系中的坐标,不是Bitmap本身的局部坐标系。我们在代码中保证了dstRecF的长宽比与srcRect中的长宽比相同,这样不会导致图片长宽比例变形,效果见上图中的第二个放大的图形。

    3. 此处有一点需要说明,在绘图结束退出Activity的时候,我们需要调用bitmap的recyle()方法,防止内存泄露,本程序在onDestroy()方法中执行了该方法。


    总结

    1. Canvas通过drawXXX等一些列的绘图方法决定了要绘制的图形的外形,我们可以通过自由组合绘制出我们想要的效果。drawXXX方法中的坐标都是基于当前绘图坐标系的坐标,而非Canvas坐标系,默认情况下二者重合。通过调用translate、rotate、scale等方法可以对绘图坐标系进行变换。

    2. 画笔Paint控制着所绘制的图形的具体外观,Paint默认的字体大小为12px,在绘制文本时我们往往要考虑密度density设置合适的字体大小。画笔的默认颜色为黑色,默认的style为FILL,默认的cap为BUTT,默认的线宽为0,参见下图所示: 
      这里写图片描述

    3. 在画面状的图形时,如果Paint的style是FILL,那么绘制的就是填充面;如果是STROKE,那么绘制的就是轮廓线。

    展开全文
  • html5中的canvas标签

    千次阅读 2019-04-28 15:46:51
    canvas基本用法 什么是canvas(画布) 是 HTML5 新增的元素,可用于通过使用JavaScript中的脚本来绘制图形 例如,它可以用于绘制图形,创建动画。 最早由Apple引入WebKit 我们可以使用标签来定义一个canvas元素 ---...
  • 学习HTML5 Canvas这一篇文章就够了

    万次阅读 多人点赞 2017-06-16 20:57:43
    一、canvas简介&amp;amp;lt;canvas&amp;amp;gt; 是 HTML5 新增的,一个可以使用脚本(通常为JavaScript)在其中绘制图像的 HTML 元素。它可以用来制作照片集或者制作简单(也不是那么简单)的动画,甚至可以进行...
  • canvas+vue实现图片的缩放、拖拽

    千次阅读 2020-05-27 18:01:31
    但是我们还有其他需求啊,在底层图片上添加一些图片或者文字注释,并且随着底层图片的拖动和缩放后,其相对位置不能发生改变,因此选择了用canvas解决,毕竟x,y坐标在那里,敌动我也动。 定义canvas,解决绘制图片...
  • Canvas线条动画

    千次阅读 多人点赞 2020-09-03 19:23:39
    Canvas线条动画  简单的Canvas动画,代码只有71行,试试吧!没有谁天生就会,用这个动画,做为你的Canvas小结。 如果你觉得我的代码还算有趣,在你的学习中能有所帮助,请查看我的置顶文章,我由衷感谢! <!...
  • 360度全景图-HTML5 Canvas实现

    千次下载 热门讨论 2013-12-06 20:00:31
    360度全景图,打开fullview.html即可运行 支持chrome/FF/IE9+
  • vue canvas 基础入门

    万次阅读 2020-05-26 10:56:56
    vue canvas 基础入门vue canvas 基础入门Canvas Apicanvas Init 初始化canvas 绘制 vue canvas 基础入门 Canvas Api 话不多说、先了解下canas 的api https://www.canvasapi.cn/ 可以看出,canvas 的api 还不是很复杂...
  • canvas绘制的流程图

    2013-01-03 20:01:10
    canvas绘制的流程图 未实现交互 可用于熟悉html5中的canvas
  • canvas详解(2)-事件处理

    千次阅读 2018-08-13 10:35:08
    canvas详解(2)-事件处理 上一章我们讲解了canvas的基本原理应用,这一章主要讲解一下事件如何处理。 canvas详解(1)-原理 canvas因为是画布的原因,实际上我们可以将它当做一张图片,所以在html中,无论这个...
  • LVGL『Canvas画布控件』介绍

    千次阅读 2021-02-21 10:40:15
    一....画布的主要部分称为 LV_CANVAS_PART_MAIN ,只有 image_recolor 属性用于为LV_IMG_CF_ALPHA_1/2/4/8BIT 图像赋予颜色。 三. LVGL GU画布控件的使用 1. 缓冲(Buffer) 画布需要一个缓冲区来
  • HTML5 canvas 设置背景图

    万次阅读 2019-01-18 18:19:14
    页面代码如下 &lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta charset="utf-8"...HTML5 canvas 设置背景图&...canvas2image.js"&gt;&lt

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 321,298
精华内容 128,519
关键字:

canvas