1.1 如何等待所有图片加载完成后,才开始绘图?
多张图片绘制需要按照特定的顺序,而加载完成顺序是完全无法预测,只能等待所有图片加载完成,才能开始绘制。
/***************************
//典型的错误代码
var img1 = new Image();
img1.src = "";
img1.onload = function(){
ctx.drawImage(img1, x, y);
}
var img2 = new Image();
img2.src = "";
img2.onload = function(){
ctx.drawImage(img2, x, y);
}
******************************/
var progress = 0; //加载进度 0~100
var img1 = new Image();
img1.src = "";
img1.onload = function(){
progress += 80;
if(progress===100){
startDraw();
}
}
var img2 = new Image();
img2.src = "";
img2.onload = function(){
progress += 20;
if(progress===100){
startDraw();
}
}
1.2 如何为Canvas上的图形/图像绑定事件监听?——难点
网页只能为DOM元素绑定监听函数,Canvas绘图技术中只要一个Canvas元素,其它图形图像都不是元素——无法进行事件绑定!——
解决办法:为Canvas绑定监听函数,获取事件发生的坐标,是否处于目标图形/图像所在范围内——只能为规则的图形“绑定”监听
Adobe Photoshop:处理位图——每幅图像由点(rgb)组成,善于描述颜色的细节变化,可用于照片等领域——放大后会出现马赛克失真。 Adobe Illustrator:处理矢量图——每幅图像由线条(需要指定方向、值),可以无限缩放而不失真——不善于描述颜色的细节变化。 |
1.3 HTML5新特性之五——SVG绘图
Canvas绘图 |
SVG绘图 |
|
绘图类型 |
位图 |
矢量图 |
缩放 |
失真 |
不失真 |
颜色细节 |
丰富 |
不够丰富 |
应用领域 |
照片、游戏 |
统计图、图标、地图 |
内容 |
JS绘制 |
每个图形都是标签 |
事件绑定 |
不方便 |
方便 |
Scalable Vector Graphiph,可缩放的矢量图,此技术在2000年就已经存在了,独立于网页的一门技术;HTML5之后,纳入了HTML5标准标签库,并进行了一定的瘦身。
SVG技术的使用方法:
(1)HTML5之前的使用方法:
SVG标签不属于HTML4或XHTML标签,只能编写在独立的XML文件中,首先编写一个SVG文件(本质是一个XML文件),然后在HTML中使用IMG/IFRAME/OBJECT/EMBED等标签引入.svg文件
(2)HTML5之后的使用方法——H5已经把SVG标签采纳:
在HTML文件中直接使用SVG相关标签即可
<svg>默认为300*150的inline-block</svg>
练习:
(1)使用SVG矩形绘制一个国际象棋的棋盘——使用HTML中的rect标签
(2)使用SVG矩形绘制一个国际象棋的棋盘——使用JS动态创建rect标签
1.4 使用SVG绘制矩形
<rect width="" height="" x="" y="" fill="" fill-opacity="" stroke="" stroke-width="" stroke-opacity=""></rect>
注意:
(1)SVG图形的样式可以用HTML属性赋值,也可以使用CSS形式,但不接受普通的CSS属性!只能使用SVG元素的专有样式属性。
(2)SVG图形的属性不属于HTMLDOM标准,只能使用核心DOM方法操作其属性: rect.setAttribute('','')
(3)使用JS动态创建SVG元素,1)用svg.innerHTML ='' 2)用document.createElementNS('', ''),不能使用document.createElement()
(4)SVG元素的nodeName都是纯小写形式!与普通的HTML元素不同!
练习:
(1)在SVG画布上绘制一个矩形,从左移动到右,同时填充颜色还在不停的随机改变
(2)根据如下的JSON数据,绘制柱状统计图——有坑!!
[
{"label": "1月", "value": 350},
{"label": "2月", "value": 300},
{"label": "3月", "value": 450},
{"label": "4月", "value": 380}
]
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
body {
text-align: center;
}
svg {
background: #f0f0f0;
}
</style>
</head>
<body>
<h3>使用SVG绘图——矩形</h3>
<svg width="600" height="400" id="svg10">
</svg>
<script>
var data = [
{"label": "1月", "value": 250},
{"label": "2月", "value": 200},
{"label": "3月", "value": 350},
{"label": "4月", "value": 280}
];
var html = '';
for(var i=0; i<data.length; i++){
var d = data[i];
//动态创建的svg元素,必须指定所在的xmlns
// var rect=document.createElement('rect');
var rect=document.createElementNS('http://www.w3.org/2000/svg','rect');
rect.setAttribute('width',50);
rect.setAttribute('height',d.value);
rect.setAttribute('x',i*80);
rect.setAttribute('y',10);
svg10.appendChild(rect);
}
svg9.innerHTML = html;
</script>
</body>
</html>
1.5 使用SVG绘制圆形
<circle r="" cx="" cy=""></circle>
练习:
(1)创建30个大小随机、颜色随机、透明度随机的圆形
提示:使用createElementNS()创建元素,setAttribute()修改属性
(2)点击某个圆形,其慢慢变大、变淡...... 直至消失,从DOM树上删除该元素
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
body {
text-align: center;
}
svg {
background: #f0f0f0;
}
</style>
</head>
<body>
<h3>使用SVG绘图——圆</h3>
<svg width="600" height="400" id="svg12">
</svg>
<script>
//动态添加30个圆形
for(var i=0;i<30;i++){
var c=document.createElementNS('http://www.w3.org/2000/svg','circle');
c.setAttribute('r',rn(10,80));
c.setAttribute('cx',rn(0,600));
c.setAttribute('cy',rn(0,400));
c.setAttribute('fill',rc(0,256));
c.setAttribute('fill-opacity',Math.random());
svg12.appendChild(c);
}
//使用事件代理方式为每个圆形绑定单击监听函数
svg12.onclick=function (e) {
var target=e.target;
if(target.nodeName==='circle'){
var timer=setInterval(function () {
// target.setAttribute('data-animate','true');
//让半径变大
var r=target.getAttribute('r');
r=parseFloat(r);
r*=1.1;
target.setAttribute('r',r);
//让透明度变小
var p=target.getAttribute('fill-opacity');
p=parseFloat(p);
p*=0.9;
target.setAttribute('fill-opacity',p);
if(p<0.001){ //几乎看不见时删除
clearInterval(timer);
svg12.removeChild(target);
}
},30);
}
}
// random number:生成指定范围内的随机整数
function rn(min,max) {
return Math.floor(Math.random()*(max-min)+min);
}
//random color:生成指定范围内的随机颜色
function rc(min,max) {
var r=rn(min,max);
var g=rn(min,max);
var b=rn(min,max);
return `rgb(${r},${g},${b})`;
}
</script>
</body>
</html>
(3)实现“蜻蜓点水”效果,点击svg画布的某处,即在此处生成一个圆形,立即变大、变淡...直至消失
1.6 使用SVG绘制椭圆
<ellipse rx="" ry="" cx="" cy=""></ellipse>
1.7 使用SVG绘制直线
<line x1="" y1="" x2="" y2="" stroke=""></line>
注意:所有的SVG图形默认只有填充色,没有描边色。
练习:使用line创建如下的图标:
提示:可以把多个元素放在一个<g></g>小组中,可以自动继承小组的公共属性
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
body {
text-align: center;
}
svg {
background: #f0f0f0;
}
</style>
</head>
<body>
<h3>使用SVG绘图</h3>
<svg width="600" height="400" id="svg15">
<!--group:小组-->
<g stroke="#800" stroke-width="50">
<line x1="100" y1="100" x2="200" y2="100"></line>
<line x1="250" y1="100" x2="500" y2="100"></line>
<line x1="100" y1="200" x2="200" y2="200"></line>
<line x1="250" y1="200" x2="500" y2="200"></line>
<line x1="100" y1="300" x2="200" y2="300"></line>
<line x1="250" y1="300" x2="500" y2="300"></line>
</g>
</svg>
<script>
</script>
</body>
</html>
1.8 使用SVG绘制折线
<polyline points="x1,y1 x2,y2 x3,y3 ....." stroke="" fill-opacity="0"></polyline>
练习:使用折线绘制如下的图标
1.9使用SVG绘制多边形
<polygon points="x1,y1 x2,y2 ...."></polygon>
练习:使用多边形绘制如下的图标:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
body {
text-align: center;
}
svg {
background: #f0f0f0;
}
</style>
</head>
<body>
<h3>使用SVG绘图——信封</h3>
<svg width="600" height="400" id="svg15">
<g fill="#030">
<polygon points="100,100 300,200 500,100"></polygon>
<polygon points="100,150 100,300 500,300 500,150 300,250" ></polygon>
</g>
</svg>
<script>
</script>
</body>
</html>
1.10 使用SVG绘制文本
提示:传统的标签不能置于SVG内部!同理,SVG的标签也不能放在其它元素内部!
<text x="" y="" font-size="" alignment-baseline="before-edge">文本内容</text>
5.11使用SVG绘制图像
提示:在SVG中绘制图像使用image元素,引入位图后,此SVG图片放大后会失真。
<image width="" height="" xlink:href="x.jpg" x="" y=""></image>
1.12 如何使用渐变效果
<svg width="600" height="400" id="svg15">
<!--渐变属于特效,必须声明在“特效列表”-->
<defs>
<linearGradient id="g1" x1="0" y1="0" x2="100%" y2="0">
<stop offset="0" stop-color="#f00"></stop>
<stop offset="100%" stop-color="#0f0"></stop>
</linearGradient>
</defs>
<rect fill="url(#g1)"></rect>
</svg>
课后练习:
-
假设前端页面获取到后台返回了如下的JSON数据:
'[{"label":"HTML",value:3}, {"label":"CSS",value:5},....]'
根据这段JSON字符串,绘制下图:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
body{
text-align: center;
}
svg{
background:#f0f0f0;
}
</style>
</head>
<body>
<h3>svg绘图--各阶段难度系数</h3>
<svg id="s1">
<!--所有的特效对象-->
<defs id="effectList">
<!--<linearGradient id="g0" x1="0" y1="0" x2="0" y2="100%">-->
<!--<stop offset="0" stop-color="#f00"></stop>-->
<!--<stop offset="100%" stop-color="#f00" stop-opacity="0"></stop>-->
<!--</linearGradient>-->
</defs>
<!--坐标轴小组-->
<g stroke="#555" stroke-width="2">
<!--x轴-->
<line x1="50" y1="450" x2="650" y2="450"></line>
<line x1="630" y1="440" x2="650" y2="450"></line>
<line x1="630" y1="460" x2="650" y2="450"></line>
<!--Y轴-->
<line x1="50" y1="450" x2="50" y2="50"></line>
<line x1="40" y1="70" x2="50" y2="50"></line>
<line x1="60" y1="70" x2="50" y2="50"></line>
</g>
</svg>
<script>
var w=600+100;
var h=10*40+100; //难度值*40高度倍率+padding*2
s1.setAttribute('width',w);
s1.setAttribute('height',h);
var data=[
{label:"html",value:3},
{label:"css",value:5},
{label:"js",value:7},
{label:"dom",value:6},
{label:"jquery",value:5.5},
{label:"ajax",value:8},
{label:"html5",value:5.5}
];
var colWidth=600/(2*data.length+1);
for(var i=0;i<data.length;i++){
var d=data[i]; //遍历每个数据对象
var cw=colWidth; //柱子的宽
var ch=d.value*40; //柱子的高
var x=(2*i+1)*colWidth+50;
var y=500-50-ch;
//动态添加渐变对象
var c=rc(); //渐变颜色
var html=`
<linearGradient id="g${i}" x1="0" y1="0" x2="0" y2="100%">
<stop offset="0" stop-color="${c}"></stop>
<stop offset="100%" stop-color="${c}" stop-opacity="0"></stop>
</linearGradient>`;
effectList.innerHTML+=html;
//动态创建矩形
var rect=document.createElementNS('http://www.w3.org/2000/svg','rect');
rect.setAttribute('width',cw);
rect.setAttribute('height',ch);
rect.setAttribute('x',x);
rect.setAttribute('y',y);
rect.setAttribute('fill',`url(#g${i})`);
s1.appendChild(rect);
}
// random color
function rc() {
var r=Math.floor(Math.random()*256);
var g=Math.floor(Math.random()*256);
var b=Math.floor(Math.random()*256);
return `rgb(${r},${g},${b})`
}
</script>
</body>
</html>
2)自学two.js工具的使用,理解其作用,仿写官方示例代码,学会使用方法,实现如下效果,月亮绕着地球转,地球绕着太阳转: