canvas在手机页面上被自动放大导致模糊问题(canvas.style.width和canvas.width的区别)
这几天在移动端页面用到了canvas画折线图。遇到了两个比较关键的问题。
1.canvas.style.width跟canvas.width(同理,canvas.style.height和canvas.style.height)是两个不同的概念。
canvas.style.width是浏览器渲染canvas的尺寸,而canvas.width是canvas的画布大小。所以不要误以为在css里面设置了canvas的尺寸就OK啦。如果没有设置canvas的width和height属性,则其默认值是width:300px,height:150px。(PS:同时利用style.width,style.height,width,height可控制缩放效果)。
demo:
<!DOCTYPE html> <html> <head> <title>Demo</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript"> function drawDiagonal(id){ var canvas=document.getElementById(id); var context=canvas.getContext("2d"); context.beginPath(); context.moveTo(0,0); context.lineTo(100,100); context.stroke(); } window.onload=function(){ drawDiagonal("diagonal1"); drawDiagonal("diagonal2"); drawDiagonal("diagonal3"); } </script> </head> <body> <canvas id="diagonal1" style="border:1px solid;" width="100px" height="100px"></canvas> <canvas id="diagonal2" style="border:1px solid;width:200px;height:200px;" width="100px" height="100px"></canvas> <canvas id="diagonal3" style="border:1px solid;width:200px;height:200px;"></canvas> </body> </html>
2.canvas在手机上的效果是变模糊了,原因是被自动放大了。所以必须得把canvas放大。大家或许会问到底要放大多少。这是我们需要根据不同的手机的devicePixelRatio(设备像素比,简介文章:http://www.zhangxinxu.com/wordpress/2012/08/window-devicepixelratio/)来进行缩放canvas。
利用devicePixelRatio和第一个问题的方法,我们可以解决如题现象。
demo(手机截图):
左图模糊,右图正常。表格上的圆上用css写的,用来作对比。该demo的源代码地址是:https://files.cnblogs.com/joyho/graph.rar
左图的源代码:
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta http-equiv="Cache-Control" content="no-cache" /> <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no" /> <meta name="MobileOptimized" content="320"/> <title></title> <style> .spot{ display: block; background:#1881f0; width:18px; height:18px; border-radius: 9px; margin-left:100px; } </style> </head> <body> <span class="spot"></span> <canvas id="a_canvas" width="400" height="300"></canvas> <script type="text/javascript"> (function (){ window.addEventListener("load", function(){ var data = [80,92,104,110,68,50,45,90,74,68,98,103]; // 获取上下文 var a_canvas = document.getElementById('a_canvas'); var context = a_canvas.getContext("2d"); // 描绘边框 var grid_cols = data.length + 1; var grid_rows = 10; var cell_height = a_canvas.height / grid_rows; var cell_width = a_canvas.width / grid_cols; context.lineWidth = 1; context.strokeStyle = "#cdcdcd"; console.log("cell_width,cell_height:",cell_width,cell_height); // 结束边框描绘 context.beginPath(); // 准备画横线 for (var col = 0; col <= grid_cols; col++) { var x = col * cell_width; context.moveTo(x,0); context.lineTo(x,a_canvas.height); } // 准备画竖线 for(var row = 0; row <= grid_rows; row++){ var y = row * cell_height; context.moveTo(0,y); context.lineTo(a_canvas.width, y); } context.lineWidth = 1; context.strokeStyle = "#cdcdcd"; context.stroke(); var max_v = 0; for(var i = 0; i<data.length; i++){ if (data[i] > max_v) { max_v = data[i]}; } max_v = max_v * 1.1; // 将数据换算为坐标 var points = []; for( var i=0; i < data.length; i++){ var v= data[i]; var px = cell_width * (i +1); var py = v; points.push({"x":px,"y":py}); } // 绘制折现 context.beginPath(); context.moveTo(points[0].x, points[0].y); for(var i= 1; i< points.length; i++){ context.lineTo(points[i].x,points[i].y); console.log(points[i].x,points[i].y); } context.lineWidth = 2; context.strokeStyle = "#1881f0"; context.stroke(); //绘制坐标图形 for(var i in points){ var p = points[i]; context.beginPath(); context.arc(p.x,p.y,9,0,2*Math.PI); context.fillStyle = "#1881f0"; context.fill(); //写文字 context.textAlign = "center"; context.textBaseline = "middle"; context.fillStyle = "#fff"; context.font="12px Arial"; context.fillText("12", p.x-1,p.y-1) } },false); })(); </script> </body>
右图的源代码如下
其中解决问题的关键代码是:
var tScale = window.devicePixelRatio, tWidth = 400, tHeight = 300; a_canvas.style.width = tWidth + "px"; a_canvas.style.height = tHeight + "px"; a_canvas.width = tWidth * tScale; a_canvas.height = tHeight * tScale;
其中圆、文字、线条也需要乘以这个window.devicePixelRatio的值:
context.arc(p.x,p.y,9*tScale,0,2*Math.PI);//圆半径:9*tScale
context.font=12*tScale+"px Arial";//文字大小:12*tScale
context.lineWidth = 2;//线条的宽度:1*tScale
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta http-equiv="Cache-Control" content="no-cache" /> <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no" /> <title></title> <style> .spot{ display: block; background:#1881f0; width:18px; height:18px; border-radius: 9px; margin-left:100px; } </style> </head> <body> <span class="spot"></span> <canvas id="a_canvas"></canvas> <script type="text/javascript"> (function (){ window.addEventListener("load", function(){ var data = [80,92,104,110,68,50,45,90,74,68,98,103]; // 获取上下文 var a_canvas = document.getElementById('a_canvas'); var tScale = window.devicePixelRatio, tWidth = 400, tHeight = 300; a_canvas.style.width = tWidth + "px"; a_canvas.style.height = tHeight + "px"; a_canvas.width = tWidth * tScale; a_canvas.height = tHeight * tScale; for(var i=0;i<data.length;i++){ data[i] = data[i]*tScale; } var context = a_canvas.getContext("2d"); // 描绘边框 var grid_cols = data.length + 1; var grid_rows = 10; var cell_height = a_canvas.height / grid_rows; var cell_width = a_canvas.width / grid_cols; context.lineWidth = 1; context.strokeStyle = "#cdcdcd"; console.log("cell_width,cell_height:",cell_width,cell_height); // 结束边框描绘 context.beginPath(); // 准备画横线 for (var col = 0; col <= grid_cols; col++) { var x = col * cell_width; context.moveTo(x,0); context.lineTo(x,a_canvas.height); } // 准备画竖线 for(var row = 0; row <= grid_rows; row++){ var y = row * cell_height; context.moveTo(0,y); context.lineTo(a_canvas.width, y); } context.lineWidth = 1; context.strokeStyle = "#cdcdcd"; context.stroke(); var max_v = 0; for(var i = 0; i<data.length; i++){ if (data[i] > max_v) { max_v = data[i]}; } max_v = max_v * 1.1; // 将数据换算为坐标 var points = []; for( var i=0; i < data.length; i++){ var v= data[i]; var px = cell_width * (i +1); var py = v; points.push({"x":px,"y":py}); } // 绘制折现 context.beginPath(); context.moveTo(points[0].x, points[0].y); for(var i= 1; i< points.length; i++){ context.lineTo(points[i].x,points[i].y); console.log(points[i].x,points[i].y); } context.lineWidth = 2; context.strokeStyle = "#1881f0"; context.stroke(); //绘制坐标图形 for(var i in points){ var p = points[i]; context.beginPath(); context.arc(p.x,p.y,9*tScale,0,2*Math.PI); context.fillStyle = "#1881f0"; context.fill(); //写文字 context.textAlign = "center"; context.textBaseline = "middle"; context.fillStyle = "#fff"; context.font=12*tScale+"px Arial"; context.fillText("12", p.x-1,p.y-1) } },false); })(); </script> </body>