精华内容
下载资源
问答
  • canvas画板实例

    2018-07-05 19:24:22
    canvas画板实例,使用js+canvas。主要功能:画笔颜色、画笔粗细、橡皮擦、清除画布、撤销、导出图片。
  • vue-canvas 一个简单vue canvas画板的demo 在vue 中制作简单的画板 demo 需要 Element-ui
  • canvas画板

    2016-04-17 17:36:24
    这是一个利用H5中canvas制作的一个画板,包括铅笔,橡皮,各种几何图形等多种功能。
  • 微信小程序 Canvas 封装的画板,支持画图,签名,生成图片....,使用简单方便再次修改自定义!
  • 通过小程序canvas原生组件方法,实现对画布的基本操作并扩展相关功能,实现在线签名功能,支持翻页、撤销、清空、生成图片,并增加田字格底图
  • 实现一个canvas画板

    千次阅读 2019-03-11 11:11:29
    canvas>是HTML5新增的元素,可用于通过使用JavaScript中的脚本来绘制图形。例如,它可以用于绘制图形,制作照片,创建动画,甚至可以进行实时视频处理或渲染。 ——摘自MDN canvas MDN canvas有许多的API,...

    <canvas>是HTML5新增的元素,可用于通过使用JavaScript中的脚本来绘制图形。例如,它可以用于绘制图形,制作照片,创建动画,甚至可以进行实时视频处理或渲染。

    ——摘自MDN      canvas MDN

    canvas有许多的API,可以插入图片,可以编写文字,可以处理视频。按照MDN给出的canvasPAI,可以将canvas学的很好,但是前端学习的精髓是用到什么学什么,如果只是停留在理论以及API上的学习的话,这样很容易忘记而且不够形象。

    所以前端最好的学习方法是完成一个小作品,它要用到什么API我们就去学什么,顺带将相关的API熟悉理解。

    今天我向大家分享我的一个canvas画板小作品,作品功能包含(支持PC与移动端、画笔选色,橡皮擦、清空画板、保存画布)。


    1、首先在html中定义canvas标签。

    <meta name="viewport" content="width=device-width, initial-scale=1.0">表示宽度等于设备宽度,简单的理解:这一行适配移动端的代码。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>画板</title>
    </head>
    <body>
    <canvas id="canvas" class="cursor1" width="500" height="500"></canvas>
    <script src="canvas-demo.js"></script>
    </body>
    </html>
    

    js:取到html中canvas的id,并且声明canvas的内容为2d。

    let canvas = document.getElementById('canvas');
    let ctx = canvas.getContext('2d');
    

    到这里canvas的基础设置就完成了。

     

    2、定义画布的宽高

    我们需要设置画布的宽高,但是在html中的canvas标签设置是不够的,因为它的宽高只能定死不能随着视口的改变而变化。

    另外,用css的vw,vh长度单位来定义canvas的宽高看起来是可以的,但是它会像img图片一样拉伸变形,具体你试试就知道了。

    所以这里我们要用到js的方法。

    wh()
    //    canvas与屏幕宽高一致
    function wh() {
        let pageWidth = document.documentElement.clientWidth;
        let pageHeight = document.documentElement.clientHeight;
        canvas.width = pageWidth;
        canvas.height = pageHeight;
    }
    

    在进入页面时执行wh( )函数,获取设备的宽高,然后将值赋给canvas中的宽高,使画布与设备宽高一致。

     

    3、特性检测

    通过检测是否有document.body.ontouchstart这个API来确定是否支持触屏设备,如果支持就走触屏设备的逻辑,如果支持则走非触屏设备的逻辑。

    /特性检测
    if (document.body.ontouchstart !== undefined) {
    //触屏设备
    }else{
    //非触屏设备
    }
    

     

    4、鼠标点击事件,鼠标滑动事件,鼠标松开事件

    现在要准备开始画画了,在画画之前我们要了解一下在canvas画一笔的具体步骤(以PC端举例):

    1. 点击鼠标
    2. 滑动鼠标
    3. 松开鼠标

    一笔完成

    所以我们要监听上面三点的鼠标事件,然后拿到其x,y轴对应视口的坐标点。

    当鼠标点击时用一个变量记录下该坐标点(x1),鼠标开始滑动时记录滑动到的第一坐标点(x2),这样我们就有了2个坐标点,再通过canvas中的stroke()事件将第一个坐标点与第二个坐标点连接起来变成线。

    然后将(x2)坐标点赋值给(x1),继续滑动鼠标。这时(x1)的值为滑动到的第一个坐标点,(x2)为当前坐标点,继续连线。

    最后松开鼠标,触发我们人为设置的控制开关painting = false,循环终止,坐标轴停止赋值。一段线段便完成。

    // 非触屏设备
    // 按下鼠标(mouse)
        //鼠标点击事件(onmousedown)
        canvas.onmousedown = function (e) {
            let x = e.offsetX;
            let y = e.offsetY;
            painting = true;
            if (EraserEnabled) {
                ctx.clearRect(x - 15, y - 15, 30, 30)
            }
            startPoint = {x: x, y: y};
        };
    
    //    滑动鼠标
    //    鼠标滑动事件(onmousemove)
        canvas.onmousemove = function (e) {
            let x = e.offsetX;
            let y = e.offsetY;
            let newPoint = {x: x, y: y};
            if (painting) {
                if (EraserEnabled) {
                    ctx.clearRect(x - 15, y - 15, 30, 30)
                } else {
                    drawLine(startPoint.x, startPoint.y, newPoint.x, newPoint.y);
                }
                startPoint = newPoint;
            }
        };
    //    松开鼠标
    //    鼠标松开事件(onmouseup)
        canvas.onmouseup = function () {
            painting = false;
        };
    

     

    5、触屏设备中的开始,滑动,结束事件

    在移动端中是没有鼠标的,因此与其相对应的有一个触摸事件touch。

    步骤与步骤4同理,但需要注意一点:移动设备是支持多点触摸的,因此这里的x、y轴需要从e.touches[0]数组第一个中取。

    //触屏设备
        canvas.ontouchstart = function (e) {
            //[0]表示touch第一个触碰点
            let x = e.touches[0].clientX;
            let y = e.touches[0].clientY;
            painting = true;
            if (EraserEnabled) {
                ctx.clearRect(x - 20, y - 20, 40, 40)
            }
            startPoint = {x: x, y: y};
        };
        canvas.ontouchmove = function (e) {
            let x = e.touches[0].clientX;
            let y = e.touches[0].clientY;
            let newPoint = {x: x, y: y};
            if (painting) {
                if (EraserEnabled) {
                    ctx.clearRect(x - 15, y - 15, 30, 30)
                } else {
                    drawLine(startPoint.x, startPoint.y, newPoint.x, newPoint.y);
                }
                startPoint = newPoint;
            }
        };
        canvas.ontouchend = function () {
            painting = false;
        };
    

     

    6、橡皮擦

    通过EraserEnabled是否等于true开启橡皮擦功能

    如果等于true,则用clearRect绘制一个空白矩形,重复步骤4,点击、滑动、松开即可达到擦除的效果。

    let EraserEnabled = false;
    if (EraserEnabled) {
                    ctx.clearRect(x - 15, y - 15, 30, 30)
                } else {
                    drawLine(startPoint.x, startPoint.y, newPoint.x, newPoint.y);
                }
    

     

    7、选择画笔的颜色

    通过canvas中的strokeStyle来改变描边的颜色即可,笔画粗细同理。

    然后在css中设置样式,当点击黄色则表示黄色线条,红色则红色线条。

    线宽同理,设置为变量,控制它们的传值即可。

    //笔画颜色
        ctx.strokeStyle = 'black';
    //线宽
        ctx.lineWidth = 2;
    

     

    8、保存为图片

    先用canvas.toDataURL获取到画好的画板,然后通过a标签的download事件下载即可

    /保存
    save.onclick = function() {
        let url = canvas.toDataURL('image/jpg');
        let a = document.createElement('a');
        document.body.appendChild(a);
        a.href = url;
        a.download = '草稿纸';
        a.target = '_blank';
        a.click()
    };
    

     

    最后画板完成:

    å®ç°ä¸ä¸ªcanvaså°ç»æ¿

     

                                                                                  我的canvas在线画板

                                                                                         github源码

     

    感兴趣的同学可以点击上方源码了解详情,本人在源码上有详细注释。

     

     

    展开全文
  • Canvas画板

    2017-06-21 18:45:08
    canvas.onmouseup = function() // 当 在画板上 鼠标 松开的 时候 { canvas.onmousemove = null; //, 清除 鼠标 移动事件 } // 一直都是在 操作 画板的 id, 注意!! } var btn ...

    <!doctype html>
    <html lang="en">
    <head>
    	<meta charset="UTF-8" />
    	<title>Document</title>
    	<style type="text/css">
    		*{
    			padding: 0;
    			margin: 0;
    		}
    		html{
    			height: 100%;
    		}
    		body{
    			margin-top: 100px;
    			height: 100%;
    			background: url({3E464148-F55C-2F6C-8BCE-A3FECE661628}.png) no-repeat center;
    		}
    		#canvas{
    			background: #F9F4F1;
    			cursor: url(brush.cur),auto;
    		}
    		#text{
    			width:18px;
    			text-align: center;
    		}
    		.box{
    			position: absolute;
    			/*background: rgba(0,0,0,.5);*/
    			background: grey;
    			top: 97px;
    			left: 328px;
    			color: white;
    			font-size: 12px;
    			
    		}
    	</style>
    	<script type="text/javascript">
    		window.onload = function()
    		{
    			var canvas = document.getElementById("canvas");
    			var Context = canvas.getContext('2d');
    			//设置填充颜色
    //			Context.fillStyle = 'royalblue';
    //			//x,y, w,h  在那里开始画
    //			Context.fillRect(10,20,30,30);
    //			
    //			Context.fillStyle = 'red';
    //			Context.fillRect(100,20,30,30);
    			
    			//线条粗细
    //			Context.lineWidth = '10';
    //			//线条颜色
    //			Context.strokeStyle = '#fff';
    //			//绘制圆角
    //			Context.lineJoin = 'round';
    //			// 画位置,没有填充
    //			Context.strokeRect(100,100,200,200);
    //			
    //			//填充颜色
    //			Context.fillStyle = 'red';
    //			//填充到那里
    //			Context.fillRect(100,100,200,200);
    //--------------------------------------------------------------------
    //画线
    //			Context.lineWidth = '10';
    //			//线的颜色
    //			Context.strokeStyle = 'yellow';
    //			
    //			//准备画线
    //			Context.beginPath();
    //			//画线起始位置
    //			Context.moveTo(100,50);
    //			//画线结束位置
    //			Context.lineTo(200,200);
    //			//把2点连接起来
    //			Context.stroke();
    //----------------------------------------------------------------
    //画板
    
    			Context.lineWidth = '1';
    			Context.strokeStyle = '#fff';
    			
    			canvas.onmousedown = function(e){
    			var e = e||window.event;
    //				获得鼠标按下是当前点的X轴的坐标
    				var starX = e.offsetX||e.layerX;
    //				获得鼠标按下是当前点的Y轴的坐标
    				var starY = e.offsetY||e.layerY;					
    //				console.log(starX+'|'+starY);
    			
    				//开始画线,起始位置
    				
    				
    				Context.beginPath();  //开始画画
    				Context.moveTo(starX,starY);   //设置开始画画的xy坐标 就是上面鼠标按下时的位置
    				
    				canvas.onmousemove = function(e)   //、 给画板里 加  移动事件
    				{
    					var e = e||window.event;
    					var endx = e.offsetX||e.layerX;  
    					var endy = e.offsetY||e.layerY;
    					Context.lineTo(endx,endy);   //设置结束画画的xy 坐标, 就是 鼠标一直在画板里面  一直移动的获取的 x y
    					Context.stroke();     //连接  、、 或当作结束 画画
    				}
    				
    				canvas.onmouseup = function()  // 当 在画板上 鼠标 松开的 时候
    				{
    					canvas.onmousemove = null;  //, 清除  鼠标   移动事件 
    				}
    			//  一直都是在 操作 画板的  id,  注意!!	
    			}
    		
    		
    		var btn = document.getElementById("btn");
    		btn.onclick = function()   //橡皮
    		{ 
    			canvas.style.cursor = 'url(eraser.cur),auto';  // 点击 橡皮时,   换鼠标图标
    //			clearInterval(timer);
    //			color.value = '#F9F4F1';
    			Context.strokeStyle = '#F9F4F1';  //  把 画笔颜色 换成   和 背景 一样的 颜色  
    		}
    		
    		
    		
    		var color = document.getElementById("color");
    		var t;
    		color.onclick = function()      // 选颜色
    		{
    			canvas.style.cursor = 'url(brush.cur),auto';  //点击选颜色 
    //			timer = setInterval(function(){
    //				Context.strokeStyle = color.value;  //    
    //				console.log(Context.strokeStyle);
    //			},1000)
    		}
    		
    //		var timer = setInterval(function(){
    		color.onchange = function()
    		{
    			Context.strokeStyle = color.value; //  颜色改变时 把 颜色值 给 画笔 
    		}
    			
    //		},1000)
    		
    //		var sx = setInterval(function(){
    //			Context.lineWidth = txt.value;
    //			Context.lineWidth = huabi.value;
    //			
    //		})
    		
    		
    		/
    		var huabi = document.getElementById("huabi");
    		var txt = document.getElementById("text");
    		
    		huabi.onchange = function()  //画笔 粗细
    		{
    			txt.value = huabi.value;
    			Context.lineWidth = txt.value;
    		}
    		
    		var re = document.getElementById("re");
    		re.onclick = function()  //重置画板
    		{
    //			Context.clearRect(0,0,635,400);
    			location.reload();   
    		}
    }		
    
    
    	</script>
    </head>
    <body>
    	<div class="box">
    		<canvas id="canvas" width="635" height="400"></canvas>
    			<div class="clear">
    				<input type="button" name="btn" id="btn" value="橡皮擦" />
    				选择颜色<input type="color" name="" id="color" value="#FFFFFF" />
    				选择画笔粗细<input type="range" name="" max="10" min="1" id="huabi" value="1" />
    				<input type="text" name="" id="text" value="1" />
    				<input type="button" value="重置画板" id="re"/>
    			</div>
    	</div>
    	
    </body>
    </html>



    展开全文
  • HTML5画板画图工具是一款可定义笔刷和画布的HTML5 Canvas画板画图工具。
  • canvas画板.rar

    2021-03-10 11:52:04
    canvas画板domo
  • QMLcanvas画板鼠标画图

    2018-10-29 13:38:21
    基于QML的canvas,实现画板功能,利用鼠标进行涂鸦随笔画
  • Canvas画板钢笔绘制线条特效是一款用钢笔绘制线条,创建导火线燃烧动画效果代码。
  • canvas实现的画板

    2018-05-18 08:56:07
    用html5的canvas实现的一个简易的画板,可以画直线、矩形、圆形,可以选择线条粗细和线条颜色’
  • 然后在这个底层的上面覆盖一个canvas, 这个canvas要复制图片上的内容,并且还要保存复制那个图片的高宽给canvas设置上,其实这一层就是给画板做背景的, 然后再这个画板上面还要加一层canvas,这一层是提供给学生...

    笔者从事教育软件行业,担任公司的前端开发,产品主要应用于PC端和移动端。之前遇到一个需求:老师在课堂上发布一张习题图片给学生练习的时候,学生要在移动端上接受老师发的图片,并在上面写出答案提交,然后老师接收到这张图片,又可以在上面批改。

    具体实现的需求是这样的:学生收到老师发的图片,就直接在学会设备(学生使用的是订做的有管控系统的平板)上显示,
    学生点击答题按钮或者是直接点击图片,就开始在图片上画写,功能上,我们要给学生画笔多颜色,笔画多粗细,还要有
    橡皮擦的功能。我之前的办法是直接把老师发的图片复制到canvas上,但是使用画笔会把原来的图片擦掉,这个不符合要求,
    后面我就改成了使用两个canvas层叠的方法实现了。老师下发的图片在最底层,然后在这个底层的上面覆盖一个canvas,
    这个canvas要复制图片上的内容,并且还要保存复制那个图片的高宽给canvas设置上,其实这一层就是给画板做背景的,
    然后再这个画板上面还要加一层canvas,这一层是提供给学生画画的,同样要设置一样的高宽,布局上要使用绝对定位,使两个
    canvas完全重叠在一起,这样做的目的是为了使用橡皮的时候,背景图不会被擦掉,最后把两个canvas融合到一起就搞定了,下面上代码;

     css样式部分

    *{margin: 0;padding: 0;}
    HTML,body{width: 100%;height: 100%;overflow: hidden;}
    .canvasdiv{width: 100%;height: 100%;position: absolute;display: none;
    	z-index: 5;top: 0;left: 0;background: #DADADA;overflow: hidden;-webkit-touch-callout: 
        none;-webkit-user-select: none;-khtml-user-select: none;-moz-user-select: none;
    	-ms-user-select: none;user-select: none;}
    .navbar{width: 500px;height: 40px;margin: 5px 0;position: relative;}
    .navbar a{display: inline-block;height: 40px;width: 40px;border-radius: 20px;
    	position: absolute;right: -20px;background: #B4A484;text-align: center;line-height: 
        40px;font-size: 30px;top: 0;-webkit-touch-callout: none;-webkit-user-select: none;
    	-khtml-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select:         
        none;}
    .eraseSeries{display: inline-block;height: 20px;margin: 10px 0 10px 15px;vertical-align:                 
        middle;display: none;}
    .eraseSeries input{width: 20px;height: 20px;vertical-align: middle;}
    select,.erase,.etext,.saveimg,.cancelimg{height: 30px;margin: 5px 0px 5px 15px;vertical-align: middle;}
    .erase,.etext,.saveimg,.cancelimg{padding: 0 10px;}
    textarea{width: 200px;height: 30px;background: transparent;position: absolute;display:none;top: 50px;left: 0;/*outline:none;*/border: 2px solid #c1c1c1;resize:both;}
    #eraseImg {border: solid;color: gray;border-radius: 118px;width: 5px;height: 5px;position: absolute;display: none;}
    .bottomCanvas{position: relative;left: 0;top: 0;z-index: 1;}
    .centerCanvas{background: transparent;position: absolute;left: 0;top: 50px;z-index: 2;display: none;}
    .topCanvas{background: transparent;position: absolute;left: 0;top: 50px;z-index: 3;display: none;}
    .drag{z-index: 4;}

    HTML部分 

    <!--zhuo599 下面这个img是老师发过来的图片,也就是准备在上面画的底图-->
    <img src="g.jpg" class="img" id="imgsrc" />
    		
    		<div class="canvasdiv" id="canvasdiv">
    			<div class="navbar" id="btnTeam">
                    <!--zhuo599 画笔颜色设置-->
    				<select id="color">
    					<option class="opt" value="red">红色</option>
    					<option class="opt" value="yellow">黄色</option>
    					<option class="opt" value="blue">蓝色</option>
    					<option class="opt" value="black" selected>黑色</option>
    					<option class="opt" value="green">绿色</option>
    				</select>
    				<select id="fontSize">
    					<!--zhuo599 画笔大小-->
    					<option value="5" selected>5</option>
    					<option value="10">10</option>
    					<option value="15">15</option>
    					<option value="20">20</option>
    					<option value="30">30</option>
    				</select>
    				<button id="erase" class="erase">擦皮</button>
    				<!--zhuo599 橡皮按钮-->
    				<div class="eraseSeries" id="eraseSeries">
    					<!--zhuo599 橡皮大小-->
    					<input type="radio" name="eraseSize" value="5" checked="checked" />5
    					<input type="radio" name="eraseSize" value="10" />10
    					<input type="radio" name="eraseSize" value="15" /> 15
    					<input type="radio" name="eraseSize" value="20" /> 20
    					<input type="radio" name="eraseSize" value="30" />30
    				</div>
    				<button id="etext" class="etext">文字</button>
    				<button id="saveimg" class="saveimg" onclick="saveImg()">保存</button>
    				<button id="cancel" class="cancelimg" onclick="cancelImg()">取消</button>
    			</div>
    			<canvas id="myCanvas" class="bottomCanvas"></canvas>
    			<canvas id="myCanvas3" class="topCanvas"></canvas>
    			<div id="eraseImg">
    				<!--zhuo599 橡皮形状-->
    			</div>
    			<textarea id="textareaInput" class="drag"></textarea>
    		</div>
    		<img src="" id="tupian"  />

    JavaScript部分

    var sizeE;//橡皮擦大小
    var fontSize;//笔画 字体大小
    var allColor; //颜色
    var mousePress = false;//画笔开关
    var last = null;
    var capi = false;//擦皮开关
    var inText = false;//文本框开关
    var ziX,ziY;//文字具体位置
    
    var canvas = document.querySelector("#myCanvas");
    var cxt = canvas.getContext("2d");
    //var canvas2 = document.getElementById('myCanvas2');
    var canvas3 = document.getElementById('myCanvas3');
    /*zhuo599 第一步点击图片生成两个canvas层  start*/
    var imgsrc = document.getElementById("imgsrc");
    function imgTest(){
    	var ele = this;
    	document.getElementById("canvasdiv").style.display = "block";
    	//canvas2.style.display = "block";
    	console.log(ele.clientWidth,ele.clientHeight);
    	canvas3.style.display = "block";
    	canvas3.width = ele.clientWidth;
    	canvas3.height = ele.clientHeight;
    	
        canvas.width= ele.clientWidth;
        canvas.height= ele.clientHeight;
        cxt.drawImage(ele, 0, 0, ele.clientWidth, ele.clientHeight);
    }
    imgsrc.onclick = imgTest;
    imgsrc.addEventListener("touchstart",imgTest,false);
    /* 第一步点击图片生成两个canvas层  end*/
    
    /*zhuo599 第二步更改橡皮大小  start*/
    var eraseImg = document.getElementById("eraseImg");
    var size = document.getElementsByName("eraseSize");
    function caPi(){
    	for (var i = 0; i < size.length; i++) {//获取到橡皮单选按钮组的选中值 
    		if (size[i].checked == true) {
    			sizeE = size[i].value; //将该值传到全局变量上,sizeE需要用来控制橡皮样式的位置 
    			eraseImg.style.width = size[i].value +"px";//橡皮样式大小发生改变 
    			eraseImg.style.height = size[i].value +"px";
    		}
    	}
    }
    
    /* 第二步更改橡皮大小  end*/
    
    /*zhuo599 第三步更改大小  start*/
    var fontS = document.getElementById("fontSize");
    function fnFontsize(){
    	fontSize = document.getElementById("fontSize").value;
    }
    fontS.onchange = fnFontsize;
    fontS.addEventListener("touchstart",fnFontsize,false);
    /* 第三步更改大小  end*/
    
    /*zhuo599 第四步更改颜色  start*/
    var color = document.getElementById("color");
    function fnColor(){
    	allColor = document.getElementById("color").value;
    }
    color.onchange = fnColor;
    color.addEventListener("touchstart",fnColor,false);
    /* 第四步更改颜色  end*/
    
    /*zhuo599 第五步 擦皮画笔变换  start*/
    var erase = document.getElementById("erase");
    var eraseSeries = document.getElementById("eraseSeries");
    function penRubber(e){
    	if(erase.innerText == "擦皮"){
    		eraseSeries.style.display = "inline-block";
    		erase.innerText = "画笔";
    		capi = true;
    	}else{
    		eraseSeries.style.display = "none";
    		erase.innerText = "擦皮";
    		capi = false;
    	}
    	e.preventDefault();
    }
    erase.onclick = penRubber;
    erase.addEventListener("touchstart",penRubber,false);
    /* 第五步 擦皮画笔变换  end*/
    
    fnFontsize();//页面刚进入初始化字体大小
    fnColor();//页面刚进入初始化颜色
    caPi();//初始化擦皮大小
    
    
    /*zhuo599 第六步 定位文字输入框的位置,并将文字画到canvas上  start*/
    var board = canvas3.getContext('2d');
    var textInput = document.getElementById("textareaInput");
    function beginDraw() {
    	mousePress = true;
    	
    	caPi();//拿到擦皮的大小
    	
    	if(inText){//如果文本框的开关是打开的,那么在点击canvas3的时候会出现一个文本框用来输入文字
    		mousePress = false;//文本框开启的时候关闭画笔功能
    		var event = window.event||event;
    		if(isTouch(event)) {
    			x = event.touches[0].pageX;
    			y = event.touches[0].pageY;
    			//console.log("手机"+x+",,,"+y);
    		} else {
    			x = event.offsetX + event.target.offsetLeft;
    			y = event.offsetY + event.target.offsetTop;
    		}
    		textInput.style.display = "inline-block";
    		textInput.style.left = x+"px";
    		textInput.style.top = y+"px";
    		ziX = x;
    		ziY = y;//保存位置,为文字显示提供具体位置
    		console.log(ziX,ziY);
    		inText = false;//用完关闭文本框开关
    		
    		var type = event.type;
    		if(type.indexOf('touch') >= 0) {//说明是手机事件,需要阻止冒泡
    			event.preventDefault();
    		}
    	}else{
    		writeZi();
    	}
    }
    function writeZi(){
    	console.log("画出文字了",ziX,ziY);
    	if(ziX != undefined && ziY != undefined){
    		board.font = fontSize+"px 宋体";
    		board.fillStyle = allColor;
    		board.fillText(textInput.value,ziX+2,ziY-36);
    		textInput.style.display = "none";
    		textInput.style.top = "50px";
    		textInput.style.left = "0px";
    		textInput.value = "";
    	}
    }
    textInput.onblur = writeZi;
    /* 第六步 定位文字输入框的位置,并将文字画到canvas上  end*/
    
    /*zhuo599 第七步 画笔橡皮擦切换  start*/
    function drawing() {
    	board.lineWidth = fontSize;//全局画笔宽度
    	if(!capi){//画笔
    		console.log(fontSize,allColor);
    		board.strokeStyle = allColor;
    		//console.log(fontSize,allColor);
    		var event = window.event||event;
    		event.preventDefault();
    		if(!mousePress) return;
    		var xy = pos(event);
    		if(last != null) {
    			board.beginPath();
    			board.lineCap="round";
    			board.moveTo(last.x, last.y);
    			board.lineTo(xy.x, xy.y);
    			board.stroke();
    		}
    		last = xy;
    	}else{//橡皮
    		var event = window.event||event;
    		event.preventDefault();
    		if(!mousePress) return;
    		var xy = pos(event);
    		if(last != null) {
    			board.clearRect(xy.x, xy.y, sizeE, sizeE);
    		}
    		last = xy;
    	}
    	
    }
    /* 第七步 画笔橡皮擦切换  end*/
    
    /*zhuo599 阻止事件冒泡和确定需要画写的位置  start*/
    function endDraw() {
    	var event = window.event||event;
    	mousePress = false;
    	event.preventDefault();
    	last = null;
    }
    function pos(event) {
    	var x, y;
    	if(isTouch(event)) {
    		x = event.touches[0].pageX;
    		y = event.touches[0].pageY-50;
    		//console.log("手机"+x+",,,"+y);
    	} else {
    		x = event.offsetX + event.target.offsetLeft;
    		y = event.offsetY + event.target.offsetTop-50;
    	}
    	// log('x='+x+' y='+y);
    	return {
    		x: x,
    		y: y
    	};
    }
    function isTouch(event) {
    	var type = event.type;
    	//console.log(type);
    	if(type.indexOf('touch') >= 0) {
    		return true;
    	} else {
    		return false;
    	}
    }
    canvas3.onmousedown = beginDraw;
    canvas3.onmousemove = drawing;
    canvas3.onmouseup = endDraw;
    canvas3.addEventListener('touchstart', beginDraw, false);
    canvas3.addEventListener('touchmove', drawing, false);
    canvas3.addEventListener('touchend', endDraw, false);
    /* 阻止事件冒泡和确定需要画写的位置  end*/
    
    /*zhuo599 第八步在topCanvas层上点击出文本框  start*/
    var inputText = document.getElementById("etext");
    function displayInput(){
    	inText = true;
    }
    inputText.onclick = displayInput;
    inputText.addEventListener('touchstart', displayInput, false);
    /* 第八步在topCanvas层上点击出文本框  end*/
    
    
    /**
     * vizhuowd
     * 保存图片方法
     */
    function saveImg(){
    	cxt.drawImage(canvas3, 0, 0, 639, 640);
    	//base64
    	var dataUrl = canvas.toDataURL();
    	document.getElementById("tupian").src = dataUrl;
    }
    
    /**
     * vizhuowd
     * 取消方法
     */
    function cancelImg(){
    	board.clearRect(0, 0, canvas3.width, canvas3.height);
    	document.getElementById("canvasdiv").style.display = "none";
    	canvas3.style.display = "none";
    }
    

    PS:最开始是只支持PC端的,后面又加入了移动端的操作,一个小demo,自己在这里保存一下,也欢迎各位开发者一起互相学习讨论。这个东西后面放到正式版本中,有很多东西是要调的,我后面自己又加了文本框输入的功能,比较low,选择文本框那里要改,文字多了会超出图片范围,初始话时字体要调大,位置也要改,哈哈

    展开全文
  • Canvas画板钢笔绘制线条特效是一款用钢笔绘制线条,创建导火线燃烧动画效果代码。
  • 代码片段: button {  -webkit-appearance: none;  padding: 12px 24px;  border-radius: 0;  position: fixed;  left: 50%;  bottom: 24px;  -webkit-transform: translateX(-50%);... }
  • html canvas 画板

    2019-08-03 01:14:14
    NULL 博文链接:https://jinhailion.iteye.com/blog/1993352
  • android小进阶- 自定义view Canvas画板

    千次阅读 2018-08-10 19:50:10
     在公司系统学习RN框架+android 公众号推文学习了一段时间后,终于等到你-公司布置的一个任务 开发纯原生的白板功能,包含画笔的粗细程度,橡皮擦,清空画板功能,裁剪,包含手动裁剪,自动裁剪需求,base64导入,...

     

     

           在公司系统学习RN框架+android 公众号推文学习了一段时间后,终于等到你-公司布置的一个任务 开发纯原生的白板功能,包含画笔的粗细程度,橡皮擦,清空画板功能,裁剪,包含手动裁剪,自动裁剪需求,base64导入,导出功能。翻页功能。html代码导入canvas,啥也不多说,上源码分析和遇到的坑。

      由于还在开发中 目前功能只缺少翻页功能 ,其他功能需求基本完善,已写成demo,可供大家学习和优化,如有写不妥之处,欢迎指点一二,在此谢谢各位。

    废话什么的不多说,上源码才是硬道理。

     

    canvasDemo的架构很简单 里面就两个activity 一个base64转bitmap的工具类

    工具类代码如下:(这个我就不分析了,自行度娘,很多,就是一个base64和bitmap互转的工具类)

    package com.example.test.canvas;
    
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.util.Base64;
    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    
    public class Base64BitmapUtil {
    
        /**
         * bitmap转为base64
         *
         * @param bitmap
         * @return
         */
        public static String bitmapToBase64(Bitmap bitmap) {
    
            String result = null;
            ByteArrayOutputStream baos = null;
            try {
                if (bitmap != null) {
                    baos = new ByteArrayOutputStream();
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
    
                    baos.flush();
                    baos.close();
    
                    byte[] bitmapBytes = baos.toByteArray();
                    result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (baos != null) {
                        baos.flush();
                        baos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return result;
        }
    
        /**
         * base64转为bitmap
         *
         * @param base64Data
         * @return
         */
        public static Bitmap base64ToBitmap(String base64Data) {
            byte[] bytes = Base64.decode(base64Data, Base64.DEFAULT);
            return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
        }
    
    }

    MainActivity 就是白板  photoAty 这个是保存图片 裁剪后展示的activity,如需更改其他需求,欢迎各位大大自行修改,页面很丑 其他的什么效果可以自行修改 。

    该为MainActivity的界面布局,很简单,一个线性布局里面放了一些功能按键,然后就是一整个白板 在imageview呈现

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#ffffff"
        android:orientation="vertical"
        tools:context=".MainActivity">
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom">
    
            <Button
                android:id="@+id/btn_resume"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="清空"
                android:textSize="14sp" />
    
            <Button
                android:id="@+id/btn_xi"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_toRightOf="@+id/btn_resume"
                android:text="细铅笔"
                android:textSize="14sp" />
    
            <Button
                android:id="@+id/btn_cu"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_toRightOf="@+id/btn_xi"
                android:text="粗铅笔"
                android:textSize="14sp" />
    
            <Button
                android:id="@+id/btn_color"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/btn_cu"
                android:text="切换颜色"
                android:textSize="14sp" />
    
            <Button
                android:id="@+id/btn_clear"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/btn_cu"
                android:layout_toRightOf="@+id/btn_color"
                android:text="橡皮擦"
                android:textSize="14sp" />
    
            <Button
                android:id="@+id/btn_autoclip"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/btn_xi"
                android:layout_toRightOf="@+id/btn_clear"
                android:text="自动裁剪"
                android:textSize="14sp" />
    
            <Button
                android:id="@+id/btn_clip"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/btn_cu"
                android:layout_toRightOf="@+id/btn_autoclip"
                android:text="手动裁剪"
                android:textSize="14sp" />
    
            <Button
                android:id="@+id/btn_extend"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/btn_color"
                android:text="延长"
                android:textSize="14sp" />
    
            <Button
                android:id="@+id/btn_import"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/btn_clear"
                android:layout_toRightOf="@+id/btn_extend"
                android:text="Base64导入"
                android:textSize="14sp" />
    
            <Button
                android:id="@+id/btn_output"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/btn_autoclip"
                android:layout_toRightOf="@+id/btn_import"
                android:text="base64导出"
                android:textSize="14sp"
                android:visibility="gone" />
    
            <Button
                android:id="@+id/btn_html"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/btn_clip"
                android:layout_toRightOf="@+id/btn_import"
                android:text="html"
                android:textSize="14sp" />
        </RelativeLayout>
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
    
        <WebView
            android:id="@+id/btn_webview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:visibility="visible"></WebView>
    
    
    
        <ImageView
            android:id="@+id/iv_canvas"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>
    </LinearLayout>

           界面长这洋,原谅像素不清晰哈 

     

    package com.example.test.canvas;
    
    import android.Manifest;
    import android.app.Activity;
    import android.content.Intent;
    import android.content.pm.PackageManager;
    import android.database.Cursor;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.PorterDuff;
    import android.graphics.PorterDuffXfermode;
    import android.graphics.Rect;
    import android.net.Uri;
    import android.os.Build;
    import android.os.CpuUsageInfo;
    import android.os.Environment;
    import android.provider.MediaStore;
    import android.support.v4.app.ActivityCompat;
    import android.support.v4.content.ContextCompat;
    import android.support.v4.content.FileProvider;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.DisplayMetrics;
    import android.util.Log;
    import android.view.Gravity;
    import android.view.MotionEvent;
    import android.view.View;
    import android.webkit.WebView;
    import android.widget.Button;
    import android.widget.ImageView;
    import android.widget.Toast;
    
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.LinkedList;
    import java.util.List;
    
    public class MainActivity extends Activity {
        private Button btn_resume, btn_xi, btn_cu, btn_color, btn_clear, btn_autoclip, btn_clip, btn_extend, btn_import, btn_output, btn_html;  //清空 保存 细 粗 颜色 橡皮擦 自动裁剪 裁剪 延长 base64 导入 base64导出 html->canvas
        private ImageView iv_canvas;
        private Bitmap baseBitmap;
        private Canvas canvas;
        private Paint paint;
        private int color[] =
                {
                        Color.RED, Color.GREEN, Color.BLUE, Color.BLACK
                };
        private int i = 1;
        private int Mode = 0; // 0 铅笔  1 橡皮擦
        private Paint eraserPaint = new Paint();
        private static final int CROP_CODE = 3;//剪切裁剪
        private final static int MY_PERMISSIONS_REQUEST_RECORD_STORAGE = 1;
        private final static int MY_PERMISSIONS_REQUEST_RECORD_STORAGE1 = 2;
        private List<Float> xlist = new ArrayList<>(); // x 坐标集合
        private List<Float> ylist = new ArrayList<>();  // y 坐标集合
    
        private Bitmap bmp;
        private int screenWidth;
        private int screenHeight;
        private WebView btn_webview;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            DisplayMetrics dm = getResources().getDisplayMetrics();
            screenWidth = dm.widthPixels;
            screenHeight = dm.heightPixels;
    
            // 初始化一个画笔,笔触宽度为3,铅笔,颜色为红色
            paint = new Paint();
            paint.setStrokeWidth(3);
            paint.setColor(Color.RED);
            paint.setAntiAlias(true);
            paint.setDither(true);
    
            // 初始化控件
            iv_canvas = (ImageView) findViewById(R.id.iv_canvas);
            btn_webview = (WebView) findViewById(R.id.btn_webview);
            btn_resume = (Button) findViewById(R.id.btn_resume);
            btn_xi = (Button) findViewById(R.id.btn_xi);
            btn_cu = (Button) findViewById(R.id.btn_cu);
            btn_color = (Button) findViewById(R.id.btn_color);
            btn_clear = (Button) findViewById(R.id.btn_clear);
            btn_autoclip = (Button) findViewById(R.id.btn_autoclip);
            btn_clip = (Button) findViewById(R.id.btn_clip);
            btn_extend = (Button) findViewById(R.id.btn_extend);
            btn_import = (Button) findViewById(R.id.btn_import);
            btn_output = (Button) findViewById(R.id.btn_output);
            btn_html = (Button) findViewById(R.id.btn_html);
    
            //添加点击监听
            btn_resume.setOnClickListener(click);
            btn_clear.setOnClickListener(click);
            btn_color.setOnClickListener(click);
            btn_cu.setOnClickListener(click);
            btn_xi.setOnClickListener(click);
            btn_html.setOnClickListener(click);
            btn_output.setOnClickListener(click);
            btn_import.setOnClickListener(click);
            btn_extend.setOnClickListener(click);
            btn_clip.setOnClickListener(click);
            btn_autoclip.setOnClickListener(click);
    
    
            iv_canvas.setOnTouchListener(touch);
        }
    
        private View.OnTouchListener touch = new View.OnTouchListener() {
            // 定义手指开始触摸的坐标
            float startX;
            float startY;
    
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    // 用户按下动作
                    case MotionEvent.ACTION_DOWN:
                        // 第一次绘图初始化内存图片,指定背景为白色
                        if (baseBitmap == null) {
                            baseBitmap = Bitmap.createBitmap(iv_canvas.getWidth(),
                                    iv_canvas.getHeight(), Bitmap.Config.ARGB_8888);
                            canvas = new Canvas(baseBitmap);
    //                        canvas.drawColor(Color.WHITE);
    
                        }
                        // 记录开始触摸的点的坐标
                        startX = event.getX();
                        startY = event.getY();
                        xlist.add((startX));
                        ylist.add((startY));
                        break;
                    // 用户手指在屏幕上移动的动作
                    case MotionEvent.ACTION_MOVE:
                        // 记录移动位置的点的坐标
                        float stopX = event.getX();
                        float stopY = event.getY();
                        xlist.add((stopX));
                        ylist.add((stopY));
                        //根据两点坐标,绘制连线
                        if (Mode == 0) {
                            canvas.drawLine(startX, startY, stopX, stopY, paint);
                            canvas.save(Canvas.ALL_SAVE_FLAG);
                            canvas.restore();
                        } else if (Mode == 1) {
                            canvas.drawLine(startX, startY, stopX, stopY, eraserPaint);
                        }
                        // 更新开始点的位置
                        startX = event.getX();
                        startY = event.getY();
                        xlist.add((startX));
                        ylist.add((startY));
                        // 把图片展示到ImageView中
                        iv_canvas.setImageBitmap(baseBitmap);
                        break;
                    case MotionEvent.ACTION_UP:
    
                        break;
                    default:
                        break;
                }
                return true;
            }
        };
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (resultCode == Activity.RESULT_OK && data != null) {
                switch (requestCode) {
                    case CROP_CODE:
                        //获取到裁剪后的图片的Uri进行处理
    
                        if (imageUri != null) {
                            Intent intent = new Intent(MainActivity.this, PhotoAty.class);
                            intent.setDataAndType(imageUri, "url");
                            startActivity(intent);
                        }
                        break;
    
                }
            }
        }
    
        private View.OnClickListener click = new View.OnClickListener() {
    
            @Override
            public void onClick(View v) {
    
                switch (v.getId()) {
                    case R.id.btn_resume:
                        resumeCanvas();
                        break;
                    case R.id.btn_cu:
                        paint.setStrokeWidth(10);
                        Mode = 0;
                        break;
                    case R.id.btn_xi:
                        paint.setStrokeWidth(3);
                        Mode = 0;
                        break;
                    case R.id.btn_color:
                        if (Mode == 0) {
                            Log.i("i的值", String.valueOf(i));
                            if (i == 4) {
                                i = 0;
                            }
                            paint.setColor(color[i]);
                            ++i;
                        }
                        break;
                    case R.id.btn_clear:
                        Mode = 1;
                        eraserPaint.setAlpha(0);
                        eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));  //设置图像的混合模式
                        eraserPaint.setAntiAlias(true);  //抗锯齿
                        eraserPaint.setDither(true);   //设置防抖动
                        eraserPaint.setStrokeCap(Paint.Cap.ROUND);   //设置线帽 圆形
                        eraserPaint.setStyle(Paint.Style.STROKE);   //设置橡皮擦样式  空心
                        eraserPaint.setStrokeJoin(Paint.Join.ROUND);
                        eraserPaint.setStrokeWidth(15); //设置橡皮擦宽度
    //                    eraserPaint.setColor(0xFF00FF00);
    //                    eraserPaint.setColor(Color.WHITE);
                        break;
                    case R.id.btn_autoclip:
                        /**
                         * 第 1 步: 检查是否有相应的权限
                         */
                        boolean isAllGranted1 = checkPermissionAllGranted(
                                new String[]{
                                        Manifest.permission.RECORD_AUDIO,
                                        Manifest.permission.CAMERA,
                                        Manifest.permission.READ_EXTERNAL_STORAGE,
                                        Manifest.permission.WRITE_EXTERNAL_STORAGE
                                }
                        );
                        if (!isAllGranted1) {
    
                            ActivityCompat.requestPermissions(MainActivity.this,
                                    new String[]{Manifest.permission.RECORD_AUDIO,
                                            Manifest.permission.CAMERA,
                                            Manifest.permission.READ_EXTERNAL_STORAGE,
                                            Manifest.permission.WRITE_EXTERNAL_STORAGE},
                                    MY_PERMISSIONS_REQUEST_RECORD_STORAGE1);
                        } else {
                            if (xlist.size() != 0 && ylist.size() != 0) {
    //                            removeDuplicate(xlist);
    //                            removeDuplicate(ylist);
                                //x 坐标最小值 最大值
                                float max_x = Float.valueOf(Collections.max(xlist));
                                float min_x = Float.valueOf(Collections.min(xlist));
                                //y 坐标最小值 最大值
                                float max_y = Float.valueOf(Collections.max(ylist));
                                float min_y = Float.valueOf(Collections.min(ylist));
    //                            canvas.clipRect(min_x, min_y, max_x, max_y);
                                float tip_x = max_x - min_x;
                                float tip_y = max_y - min_y;
                                bmp = Bitmap.createBitmap((int) (tip_x + 10),
                                        (int) (tip_y + 10), Bitmap.Config.ARGB_8888);
                                Canvas canvas1 = new Canvas(bmp);
                                canvas1.drawColor(Color.WHITE);
                                Rect mSrcRect = new Rect((int) min_x - 5, (int) min_y - 5, (int) max_x + 5, (int) max_y + 5);
                                Rect mDestRect = new Rect(0, 0, (int) (tip_x + 10), (int) (tip_y + 10));
                                canvas1.drawBitmap(baseBitmap, mSrcRect, mDestRect, null);  //把baseBitmap中需要的绘制区域mSrcRect 截取出来 放入 新创建的(宽高为需要截取的矩形区域)bmp中的mDestRect位置 在利用bmp的canvas绘制出来
    
                                canvas1.save(Canvas.ALL_SAVE_FLAG);
                                canvas1.restore();
                                saveBitmap1();
                            }
    //                        saveBitmap();
                        }
    
                        break;
                    case R.id.btn_clip:
                        /**
                         * 第 1 步: 检查是否有相应的权限
                         */
                        boolean isAllGranted = checkPermissionAllGranted(
                                new String[]{
                                        Manifest.permission.RECORD_AUDIO,
                                        Manifest.permission.CAMERA,
                                        Manifest.permission.READ_EXTERNAL_STORAGE,
                                        Manifest.permission.WRITE_EXTERNAL_STORAGE
                                }
                        );
                        if (!isAllGranted) {
    
                            ActivityCompat.requestPermissions(MainActivity.this,
                                    new String[]{Manifest.permission.RECORD_AUDIO,
                                            Manifest.permission.CAMERA,
                                            Manifest.permission.READ_EXTERNAL_STORAGE,
                                            Manifest.permission.WRITE_EXTERNAL_STORAGE},
                                    MY_PERMISSIONS_REQUEST_RECORD_STORAGE);
                        } else {
                            saveBitmap();
                        }
    
                        break;
                    case R.id.btn_extend:
    
    
                        break;
                    case R.id.btn_import:
    
                        break;
                    case R.id.btn_output:
    
    
                        break;
                    case R.id.btn_html:
                        btn_webview.setVisibility(View.VISIBLE);
                        StringBuilder sb = new StringBuilder();
                        // 拼接一段HTML代码
                        sb.append("<html>");
                        sb.append("<head>");
                        sb.append("<title> 欢迎您 </title>");
                        sb.append("</head>");
                        sb.append("<body>");
                        sb.append("<h2> 欢迎您访问<a href=\"http://www.cctv.com\">"
                                + "Java联盟</a></h2>");
                        sb.append("</body>");
                        sb.append("</html>");
                        //  加载、并显示HTML代码
                        btn_webview.loadDataWithBaseURL(null, sb.toString(), "text/html", "utf-8", null);
    
                        break;
                    default:
                        break;
                }
            }
        };
    
        /**
         * 检查是否拥有指定的所有权限
         */
        private boolean checkPermissionAllGranted(String[] permissions) {
            for (String permission : permissions) {
                if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
                    // 只要有一个权限没有被授予, 则直接返回 false
                    return false;
                }
            }
            return true;
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    
            if (requestCode == MY_PERMISSIONS_REQUEST_RECORD_STORAGE) {
                boolean isAllGranted = true;
                // 判断是否所有的权限都已经授予了
                for (int grant : grantResults) {
                    if (grant != PackageManager.PERMISSION_GRANTED) {
                        isAllGranted = false;
                        break;
                    }
                }
    
                if (isAllGranted) {
                    // 如果所有的权限都授予了, 则执行备份代码
                    saveBitmap();
    
                } else {
                    // 弹出对话框告诉用户需要权限的原因, 并引导用户去应用权限管理中手动打开权限按钮
                    android.widget.Toast.makeText(MainActivity.this, "Permission Denied", android.widget.Toast.LENGTH_SHORT).show();
                }
                return;
            }
            if (requestCode == MY_PERMISSIONS_REQUEST_RECORD_STORAGE1) {
                boolean isAllGranted = true;
                // 判断是否所有的权限都已经授予了
                for (int grant : grantResults) {
                    if (grant != PackageManager.PERMISSION_GRANTED) {
                        isAllGranted = false;
                        break;
                    }
                }
    
                if (isAllGranted) {
                    // 如果所有的权限都授予了, 则执行备份代码
                    if (xlist.size() != 0 && ylist.size() != 0) {
                        removeDuplicate(xlist);
                        removeDuplicate(ylist);
                        //x 坐标最小值 最大值
                        float max_x = Float.valueOf(Collections.max(xlist));
                        float min_x = Float.valueOf(Collections.min(xlist));
                        //y 坐标最小值 最大值
                        float max_y = Float.valueOf(Collections.max(ylist));
                        float min_y = Float.valueOf(Collections.min(ylist));
    //                            canvas.clipRect(min_x, min_y, max_x, max_y);
                        float tip_x = (max_x + Float.valueOf(5)) - min_x;
                        float tip_y = (max_y + Float.valueOf(5)) - min_y;
                        bmp = Bitmap.createBitmap((int) (max_x + 5),
                                (int) (max_y + 5), Bitmap.Config.ARGB_8888);
                        Canvas canvas1 = new Canvas(bmp);
                        canvas1.drawColor(Color.WHITE);
    
                        canvas1.drawBitmap(baseBitmap, 0, 0, null);
    
                        canvas1.save(Canvas.ALL_SAVE_FLAG);
                        canvas1.restore();
                        saveBitmap1();
                    }
    
                } else {
                    // 弹出对话框告诉用户需要权限的原因, 并引导用户去应用权限管理中手动打开权限按钮
                    android.widget.Toast.makeText(MainActivity.this, "Permission Denied", android.widget.Toast.LENGTH_SHORT).show();
                }
                return;
            }
    
        }
    
        private Uri imageUri;
    
        /**
         * 保存图片到SD卡上
         */
        protected void saveBitmap() {
    
    
            try {
                File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Canvas");        // Create the storage directory if it does not exist
                if (!imageStorageDir.exists()) {
                    imageStorageDir.mkdirs();
                }
                File file = new File(imageStorageDir + File.separator + "IMG_" + String.valueOf(System.currentTimeMillis()) + ".png");
                FileOutputStream fos = null;
                try {
                    fos = new FileOutputStream(file);
                    baseBitmap.compress(Bitmap.CompressFormat.PNG, 50, fos);  //压缩 写入
    
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                try {
                    fos.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                if (Build.VERSION.SDK_INT >= 24) {
                    imageUri = FileProvider.getUriForFile(MainActivity.this, "com.example.test.canvas.fileprovider", file);
                } else {
                    imageUri = Uri.fromFile(file);
                }
    //        File file = new File(Environment.getExternalStorageDirectory(),
    //                System.currentTimeMillis() + ".png");
    //        imageUri = Uri.fromFile(file);
                Intent intent = new Intent("com.android.camera.action.CROP");
                //添加这一句表示对目标应用临时授权该Uri所代表的文件
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                //可以选择图片类型,如果是*表明所有类型的图片
                intent.setDataAndType(imageUri, "image/*");
                // 下面这个crop = true是设置在开启的Intent中设置显示的VIEW可裁剪
                intent.putExtra("crop", "true");
                // aspectX aspectY 是宽高的比例,这里设置的是正方形(长宽比为1:1)
                intent.putExtra("aspectX", 1);
                intent.putExtra("aspectY", 1);
                // outputX outputY 是裁剪图片宽高
    //            intent.putExtra("outputX", 500);
    //            intent.putExtra("outputY", 500);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
                intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
                //是否将数据保留在Bitmap中返回,true返回bitmap,false返回uri
                intent.putExtra("return-data", false);
                //裁剪后的图片Uri路径,uritempFile为Uri类变量
    //            uritempFile = Uri.parse("file://" + "/" + Environment.getExternalStorageDirectory().getPath() + "/" + "small.jpg");
    //            intent.putExtra(MediaStore.EXTRA_OUTPUT, uritempFile);
                startActivityForResult(intent, CROP_CODE);
    
    
    //            Toast.makeText(MainActivity.this, "保存图片成功", Toast.LENGTH_SHORT).show();
            } catch (
                    Exception e)
    
            {
                Toast.makeText(MainActivity.this, "保存图片失败", Toast.LENGTH_SHORT).show();
                e.printStackTrace();
            }
    
        }
    
        private Uri imageUri1;
    
        /**
         * 保存图片到SD卡上
         */
        protected void saveBitmap1() {
    
    
            try {
                File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Canvas");        // Create the storage directory if it does not exist
                if (!imageStorageDir.exists()) {
                    imageStorageDir.mkdirs();
                }
                File file = new File(imageStorageDir + File.separator + "IMG_" + String.valueOf(System.currentTimeMillis()) + ".png");
                FileOutputStream fos = null;
                try {
                    fos = new FileOutputStream(file);
                    bmp.compress(Bitmap.CompressFormat.PNG, 50, fos);  //压缩 写入
    
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                try {
                    fos.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                if (Build.VERSION.SDK_INT >= 24) {
                    imageUri1 = FileProvider.getUriForFile(MainActivity.this, "com.example.test.canvas.fileprovider", file);
                } else {
                    imageUri1 = Uri.fromFile(file);
                }
    
                if (imageUri1 != null) {
                    Intent intent = new Intent(MainActivity.this, PhotoAty.class);
                    intent.setDataAndType(imageUri1, "url");
                    startActivity(intent);
                }
            } catch (Exception e)
    
            {
                Toast.makeText(MainActivity.this, "保存图片失败", Toast.LENGTH_SHORT).show();
                e.printStackTrace();
            }
    
        }
    
        /**
         * 清除画板
         */
        protected void resumeCanvas() {
            // 手动清除画板的绘图,重新创建一个画板
            if (baseBitmap != null) {
                baseBitmap = Bitmap.createBitmap(iv_canvas.getWidth(),
                        iv_canvas.getHeight(), Bitmap.Config.ARGB_8888);
                canvas = new Canvas(baseBitmap);
    //            canvas.drawColor(Color.WHITE);
                iv_canvas.setImageBitmap(baseBitmap);
                xlist.clear();
                ylist.clear();
                btn_webview.setVisibility(View.GONE);
                Toast.makeText(MainActivity.this, "清除画板成功,可以重新开始绘图", Toast.LENGTH_SHORT).show();
            }
        }
    
        public static List removeDuplicate(List list) {
            for (int i = 0; i < list.size() - 1; i++) {
                for (int j = list.size() - 1; j > i; j--) {
                    if (list.get(j).equals(list.get(i))) {
                        list.remove(j);
                    }
                }
            }
            return list;
        }
    }
    

    上面就是Mainactivity主界面的代码了 

    这是photoaty的布局加代码 里面逻辑很简单 就是接收MainActivity传过来的url路径进行bitmap转换然后显示出来 还有个base64导出的功能 不需要可以自行去除

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout 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"
        android:background="#D3D3D3"
        tools:context=".PhotoAty">
    
        <Button
            android:id="@+id/photo_output"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:background="#ffffff"
            android:text="base64导出"
            android:textColor="#000000"
            android:textSize="14sp" />
    
        <ImageView
            android:id="@+id/photo_img"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:scaleType="centerCrop" />
    </RelativeLayout>

     

    package com.example.test.canvas;
    
    import android.content.Intent;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.net.Uri;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    import android.widget.ImageView;
    
    import java.io.FileNotFoundException;
    
    
    public class PhotoAty extends AppCompatActivity {
    
        private ImageView photo_img;
        private Uri uri;
        private Button photo_output;
        private Bitmap bitmap;
        private String base64 = "";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_photo_aty);
            initView();
            initListener();
        }
    
    
        private void initView() {
            bitmap = null;
            base64 = "";
            photo_img = (ImageView) findViewById(R.id.photo_img);
            Intent intent = getIntent();
            uri = intent.getData();
            bitmap = decodeUriAsBitmap(uri);
            // 把解析到的位图显示出来
            photo_img.setImageBitmap(bitmap);
            photo_output = (Button) findViewById(R.id.photo_output);
        }
    
        private void initListener() {
            photo_output.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (bitmap != null) {
                        base64 = Base64BitmapUtil.bitmapToBase64(bitmap);
                        Log.i("base4------", base64);
                    }
    
                }
            });
        }
    
        private Bitmap decodeUriAsBitmap(Uri uri) {
            Bitmap bitmap = null;
            try {
                // 先通过getContentResolver方法获得一个ContentResolver实例,
                // 调用openInputStream(Uri)方法获得uri关联的数据流stream
                // 把上一步获得的数据流解析成为bitmap
                bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                return null;
            }
            return bitmap;
        }
    }
    
    

    下面来分析下代码

    首先 初始化画笔

    
            // 初始化一个画笔,笔触宽度为3,铅笔,颜色为红色
            paint = new Paint();
            paint.setStrokeWidth(3);
            paint.setColor(Color.RED);
            paint.setAntiAlias(true);

     创建画笔 设置画笔宽度,颜色,抗锯齿(使线条看起来很平滑)

          然后就是手指绘图的时候了!!!

          我们知道,所有的图案都是有一条一条非常小的线段组成,当线段组成多了就成了各种各样的形状,所以我们应该去获取每次手指移动的起点坐标x,y和终点坐标x,y 然后绘制成直线,在组合起来就是我们绘图的样子.

         所以我这边就监听手指的触摸动作事件 触摸动作事件分为3种,ACTION_DOWN(手指按住)->ACTION_MOVE(手指一动)->ACTION_UP(手指抬起) 3种动作 ,我们可以先初始化内存图片baseBitmap, 去需要绘制的区域作为宽高,然后配置Bitmap.Config.ARGB_8888 64位图(android 废除了ARGB_4444位图,采用质量更好的ARGB_8888来绘制),在创建两个参数startx,starty,分别记录手指按下去那个动作记入开始的坐标,然后在手指移动的时候记录移动的坐标,也可以看作移动的时候的终点坐标stopx,stopy,在利用canvas.drawline 绘制直线,更新新的起始坐标保存下来。然后就一直重复,就可以利用canvas绘制成我们想要的形状了,然后在显示在bitmap上 这样你手指在画板上画什么就能显示什么。

    下面贴代码

    在OnTouch回调方法里面做相应操作

      public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    // 用户按下动作
                    case MotionEvent.ACTION_DOWN:
                        // 第一次绘图初始化内存图片,指定背景为白色
                        if (baseBitmap == null) {
                            baseBitmap = Bitmap.createBitmap(iv_canvas.getWidth(),
                                    iv_canvas.getHeight(), Bitmap.Config.ARGB_8888);
                            canvas = new Canvas(baseBitmap);
                            canvas.drawColor(Color.WHITE);
                        }
                        // 记录开始触摸的点的坐标
                        startX = event.getX();
                        startY = event.getY();
                        xlist.add((startX));
                        ylist.add((startY));
                        break;
                    // 用户手指在屏幕上移动的动作
                    case MotionEvent.ACTION_MOVE:
                        // 记录移动位置的点的坐标
                        float stopX = event.getX();
                        float stopY = event.getY();
                        xlist.add((stopX));
                        ylist.add((stopY));
                        //根据两点坐标,绘制连线
                        if (Mode == 0) {
                            canvas.drawLine(startX, startY, stopX, stopY, paint);
                            canvas.save(Canvas.ALL_SAVE_FLAG);
                            canvas.restore();
                        } else if (Mode == 1) {
                            canvas.drawLine(startX, startY, stopX, stopY, eraserPaint);
                        }
                        // 更新开始点的位置
                        startX = event.getX();
                        startY = event.getY();
                        xlist.add((startX));
                        ylist.add((startY));
                        // 把图片展示到ImageView中
                        iv_canvas.setImageBitmap(baseBitmap);
                        break;
                    case MotionEvent.ACTION_UP:
    
                        break;
                    default:
                        break;
                }
                return true;
            }
        };

    然后这就是绘图白板的基本思路了,切换颜色和更换粗细想必不用我说大家应该也会了吧,利用

    paint.setStrokeWidth(variable);

    variable 填写你需要改变的画笔粗细大小 值越大越粗了,值越小当然越细了,variable等于0的时候不能再细了 画笔的细度 记住!

    切换颜色 

    paint.setColor(color);

     color 填写你需要改变的颜色就好了,就可以呈现不一样的颜色,

     现在讲下清空画板功能,也很简单。

        /**
         * 清除画板
         */
        protected void resumeCanvas() {
            // 手动清除画板的绘图,重新创建一个画板
            if (baseBitmap != null) {
                baseBitmap = Bitmap.createBitmap(iv_canvas.getWidth(),
                        iv_canvas.getHeight(), Bitmap.Config.ARGB_8888);
                canvas = new Canvas(baseBitmap);
                canvas.drawColor(Color.WHITE);
                iv_canvas.setImageBitmap(baseBitmap);
                xlist.clear();
                ylist.clear();
                Toast.makeText(MainActivity.this, "清除画板成功,可以重新开始绘图", Toast.LENGTH_SHORT).show();
            }
        }

    只要重新创建一个新的画板就好了

     现在讲解下橡皮擦功能,橡皮擦功能其实说起来也很简单,google都给我们提供好了方法

           case R.id.btn_clear:
                        Mode = 1;
                        eraserPaint.setAlpha(0);
                        eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));  //设置图像的混合模式
                        eraserPaint.setAntiAlias(true);  //抗锯齿
                        eraserPaint.setDither(true);   //设置防抖动
                        eraserPaint.setStrokeCap(Paint.Cap.ROUND);   //设置线帽 圆形
                        eraserPaint.setStyle(Paint.Style.STROKE);   //设置橡皮擦样式  空心
                        eraserPaint.setStrokeJoin(Paint.Join.ROUND);  
                        eraserPaint.setStrokeWidth(15); //设置橡皮擦宽度
    //                    eraserPaint.setColor(0xFF00FF00);
    //                    eraserPaint.setColor(Color.WHITE);
                        break;

    只要设置下透明度 ,然后设置图像的混合模式,

                        eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));  //设置图像的混合模式

    上面这句话必须设置,不然就不是橡皮擦了,不懂没关系。

    然后就可以形成这个效果

     原谅我像素不好,没拍好。

    下面讲重点了,自动裁剪功能,这个功能是因为这个白本初衷是给学生用的,学生写完后,点击提交,需要系统自动裁剪出他绘制的区域,没绘制的区域就不需要,节省文件大小。也方便查看。

    原理是这样的,我们知道画布的坐标系是从左往右 X轴,从上往下Y轴。最左上角为(0,0)坐标原点。然后先创建两个对象集合。

    private List<Float> xlist = new ArrayList<>(); // x 坐标集合
    private List<Float> ylist = new ArrayList<>();  // y 坐标集合

    分别存放绘制的所有的startx,starty,stopx,stopy。

    这样当学生绘制完毕后这两个对象集合将收集了所有的坐标点,然后取出最大的x_max,y_max.最小的x_min,y_min。然后遵循canvas.drawRect 绘制矩形区域,那么这个矩形区域就是我们裁剪后所需要的学生绘制的区域,在将这个区域放在一个新的bitmap上,这个bitmap宽高跟这个矩形区域一样大小,这样就是截取了我们需要的区域。

                 if (xlist.size() != 0 && ylist.size() != 0) {
    //                            removeDuplicate(xlist);
    //                            removeDuplicate(ylist);
                                //x 坐标最小值 最大值
                                float max_x = Float.valueOf(Collections.max(xlist));
                                float min_x = Float.valueOf(Collections.min(xlist));
                                //y 坐标最小值 最大值
                                float max_y = Float.valueOf(Collections.max(ylist));
                                float min_y = Float.valueOf(Collections.min(ylist));
    //                            canvas.clipRect(min_x, min_y, max_x, max_y);
                                float tip_x = max_x - min_x;
                                float tip_y = max_y - min_y;
                                bmp = Bitmap.createBitmap((int) (tip_x + 10),
                                        (int) (tip_y + 10), Bitmap.Config.ARGB_8888);
                                Canvas canvas1 = new Canvas(bmp);
                                canvas1.drawColor(Color.WHITE);
                                Rect mSrcRect = new Rect((int) min_x - 5, (int) min_y - 5, (int) max_x + 5, (int) max_y + 5);
                                Rect mDestRect = new Rect(0, 0, (int) (tip_x+10), (int) (tip_y+10));
                                canvas1.drawBitmap(baseBitmap, mSrcRect, mDestRect, null);  //把baseBitmap中需要的绘制区域mSrcRect 截取出来 放入 新创建的(宽高为需要截取的矩形区域)bmp中的mDestRect位置 在利用bmp的canvas绘制出来
    
                                canvas1.save(Canvas.ALL_SAVE_FLAG);
                                canvas1.restore();
                                saveBitmap1();
                            }

    //把baseBitmap中需要的绘制区域mSrcRect 截取出来 放入 新创建的(宽高为需要截取的矩形区域)bmp中的mDestRect位置 在利用bmp的canvas绘制出来 记得留点空白,这样用户看起来很舒服,不会紧贴着截取

    然后在执行savebitmap方法进行保存就行了

     protected void saveBitmap1() {
    
    
            try {
                File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Canvas");        // Create the storage directory if it does not exist
                if (!imageStorageDir.exists()) {
                    imageStorageDir.mkdirs();
                }
                File file = new File(imageStorageDir + File.separator + "IMG_" + String.valueOf(System.currentTimeMillis()) + ".png");
                FileOutputStream fos = null;
                try {
                    fos = new FileOutputStream(file);
                    bmp.compress(Bitmap.CompressFormat.PNG, 50, fos);  //压缩 写入
    
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                try {
                    fos.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                if (Build.VERSION.SDK_INT >= 24) {
                    imageUri1 = FileProvider.getUriForFile(MainActivity.this, "com.example.test.canvas.fileprovider", file);
                } else {
                    imageUri1 = Uri.fromFile(file);
                }
    
                if (imageUri1 != null) {
                    Intent intent = new Intent(MainActivity.this, PhotoAty.class);
                    intent.setDataAndType(imageUri1, "url");
                    startActivity(intent);
                }
            } catch (Exception e)
    
            {
                Toast.makeText(MainActivity.this, "保存图片失败", Toast.LENGTH_SHORT).show();
                e.printStackTrace();
            }
    
        }

    找到图片的路径地址,将bmp压缩写入进去就行了。

    这样imageUrl1就是我们自动裁剪后的图片的地址。

    大家只要拿到这个然后根据自己所需要的需求进行操作就行了。

    对了,这里要进行android系统适配和检查权限。

       

     <!-- 在SDCard中创建与删除文件权限 -->
        <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
        <!-- 往SDCard写入数据权限 -->
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


     

    android7.0 适配需要使用fileprovider,android6.0进行动态权限管理,这个我在代码里都写好了,自己去看,很简单。

    手动裁剪我动用系统自带的裁剪功能

     Intent intent = new Intent("com.android.camera.action.CROP");
                //添加这一句表示对目标应用临时授权该Uri所代表的文件
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                //可以选择图片类型,如果是*表明所有类型的图片
                intent.setDataAndType(imageUri, "image/*");
                // 下面这个crop = true是设置在开启的Intent中设置显示的VIEW可裁剪
                intent.putExtra("crop", "true");
                // aspectX aspectY 是宽高的比例,这里设置的是正方形(长宽比为1:1)
                intent.putExtra("aspectX", 1);
                intent.putExtra("aspectY", 1);
                // outputX outputY 是裁剪图片宽高
    //            intent.putExtra("outputX", 500);
    //            intent.putExtra("outputY", 500);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
                intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
                //是否将数据保留在Bitmap中返回,true返回bitmap,false返回uri
                intent.putExtra("return-data", false);
                //裁剪后的图片Uri路径,uritempFile为Uri类变量
    //            uritempFile = Uri.parse("file://" + "/" + Environment.getExternalStorageDirectory().getPath() + "/" + "small.jpg");
    //            intent.putExtra(MediaStore.EXTRA_OUTPUT, uritempFile);
                startActivityForResult(intent, CROP_CODE);

    代码里写的很清楚了,拿到图片的url地址 imageUri,传入自动自带的裁剪功能进行裁剪就好了,然后根据回调方法

       @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (resultCode == Activity.RESULT_OK && data != null) {
                switch (requestCode) {
                    case CROP_CODE:
                        //获取到裁剪后的图片的Uri进行处理
    
                        if (imageUri != null) {
                            Intent intent = new Intent(MainActivity.this, PhotoAty.class);
                            intent.setDataAndType(imageUri, "url");
                            startActivity(intent);
                        }
                        break;
    
                }
            }
        }

    做相应的操作就行了。

    这就是我的画板的功能,如有不足欢迎大家指教。非常感谢各位大大能观看到最后,写的不好见谅。

           贴上我写好的demo地址,需要的可以自行下载。

    这个demo 包含了我上面所说的所有功能,包括了android动态权限管理,android7.0系统适配,大家拿了可以直接应用在项目中。

    CSDN地址:https://download.csdn.net/download/qq_33266474/10596433

     

     

     

    展开全文
  • Canvas 画板

    2015-07-24 14:03:00
    Canvas 画板 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>canvas draw</title> </head> <body> <canvas ...
  • canvas在线画板制作

    2017-01-09 11:29:08
    这是用JavaScript技术和canvas接口写的在线画板,里面注释清楚,可以保存图片!
  • |____ javaFX系列之Canvas画板绘制组件:最简单的画板绘制图形实现 web组件(web component) css样式 UI控件(UI controls) 布局(layout) 视觉特效(visual effects) 参考 本章有且只参考了oracle javaFX官方...
  • 原生js制作的canvas画板原,刷新更换笔触的颜色,支持移动端
  • Canvas画板实例

    2020-11-24 21:15:12
    代码示例 <html> <head> <meta charset="UTF-8"> , initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>canvas实例-画板title> <style> *{ margin: 0; padding: 0; box-sizing: ...
  • canvas 画板 手写板

    2017-11-22 10:45:30
    canvas 画板 手写板 canvas 画板 手写板 canvas 画板 手写板
  • 基于qml的canvas画板功能,实现利用鼠标进行画圆画矩形等,有预览路径
  • HTML5 canvas画板的制作

    千次阅读 2017-01-09 11:21:32
    canvas画板的制作  今天看了一些canvas视频的教程,有制作画板的过程,但是我有给出他们的简洁版和使用版本,能让用户看出画直线的路径,也就是当你拉一条直线的时候就和绘图工具一样,都能很好使用,绘图之后,...
  • 一款基于HTML5 Canvas画板涂鸦
  • anvas画板上传图片后怎么可以移动图片和放大缩小,查了好几天也没找着可用的教程, 有没有大佬能教下。 可加悬赏 ``` HTML <canvas id="drawing-board"></canvas> ;"> ;"> ;"> ;"> ;"> ;"> ...
  • canvas 画板

    2021-04-03 15:44:09
    canvas画布制作画板,包括修改线条颜色,画布颜色,线条粗细,清除画布等功能 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,527
精华内容 3,010
关键字:

canvas画板