本文中,我们添加了在 HTML5 画布上选择颜色和绘制线条、圆形和正方形的功能!
绘图过程如何工作
为了在画布上绘图,我们需要知道
将这些属性添加到 Canvas 类的构造函数中:
this.activeColor = '#000000';
this.startPoint = null;
this.endPoint = null;
this.pointMode = 'start';
this.mode = 'Line';
所以我们默认画一条黑线。 现在我们添加一个函数,让我们改变我们绘制的颜色,作为 Canvas 类的一个方法:
setColor(color) {
this.activeColor = color;
this.ctx.strokeStyle = color;
this.ctx.fillStyle = color;
}
使调色板工作
现在我们有了 setColor 方法,我们可以完成 Palette.draw() 方法。 我们将画布对象添加为参数,并将 onclick 事件处理程序附加到每个调色板方块:
draw(canvas) {
const row1 = document.querySelectorAll('#row-1 .palette');
const row2 = document.querySelectorAll('#row-2 .palette');
row1.forEach((div, idx) => {
div.style.backgroundColor = this.colors[0][idx];
div.onclick = e => canvas.setColor(this.colors[0][idx]);
});
row2.forEach((div, idx) => {
div.style.backgroundColor = this.colors[1][idx];
div.onclick = e => canvas.setColor(this.colors[1][idx]);
});
}
由于我们在这里添加了它作为参数,所以我们需要在 index.html 文件中进行设置:
palette.draw(canvas);
选择要绘制的形状
该项目允许绘制 5 种形状:线条、空心圆、实心圆、空心矩形和实心矩形。 我们默认为一行。 为了改变形状(代码中称为模式),首先将此方法添加到 Canvas :
setMode(mode) {
this.mode = mode;
}
然后在 HTML 中,在调色板下方,添加一行按钮,让我们选择要绘制的形状:
<div id="draw-methods">
<button onclick="canvas.setMode('Line')">Line</button>
<button onclick="canvas.setMode('Hollow Rectangle')">Hollow Rectangle</button>
<button onclick="canvas.setMode('Filled Rectangle')">Filled Rectangle</button>
<button onclick="canvas.setMode('Hollow Circle')">Hollow Circle</button>
<button onclick="canvas.setMode('Filled Circle')">Filled Circle</button>
</div>
处理要绘制的画布上的点击
这是 Canvas 的 handleDraw 方法。 我会给你代码然后解释它在做什么,因为它比我们目前看到的更复杂。
handleDraw(e) {
const rect = this.canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top; if (this.pointMode == 'start') {
this.startPoint = [x, y];
this.pointMode = 'end';
} else if (this.pointMode == 'end') {
this.pointMode = 'start';
this.endPoint = [x, y];
// do the drawing
if (this.mode == 'Line') {
this.drawLine(this.startPoint, this.endPoint);
} else if (this.mode == 'Hollow Rectangle') {
this.drawHollowRectangle(this.startPoint, this.endPoint);
} else if (this.mode == 'Filled Rectangle') {
this.drawFilledRectangle(this.startPoint, this.endPoint);
} else if (this.mode == 'Hollow Circle') {
this.drawHollowCircle(this.startPoint, this.endPoint);
} else if (this.mode == 'Filled Circle') {
this.drawFilledCircle(this.startPoint, this.endPoint);
}
this.startPoint = null;
this.endPoint = null;
}
}
前三行是关于确定用户点击的位置。您可能会认为,当您单击画布时,鼠标单击事件只会包含您在画布中单击的位置的坐标。但这并不那么容易。你必须自己计算。
getBoundingRectClient 返回值,例如画布从左上角到左下角的距离。页面的左上角是 (0, 0),越往右越往下,数值越大。 e是传递给函数的参数,代表鼠标点击事件。 clientX 和 clientY 代表您在页面上单击的位置。减去画布元素的偏移量可以得到鼠标在画布元素中的位置。
一旦我们有了点击的位置,我们就需要知道这是第一次(“开始”)还是第二次(“结束”)点击。每个形状都是通过两次点击绘制的。对于一条线,只需选择起点和终点。对于圆,选择中心和边缘。对于矩形,选择两个对角。如果是第一次单击,则存储单击的位置,但不执行任何操作。如果是第二次单击,则存储其位置,绘制形状,然后忘记位置。
绘制形状的方法可能看起来比实际复杂。对于 drawLine ,它很简单:
drawLine(startPoint, endPoint) {
this.ctx.beginPath();
this.ctx.moveTo(startPoint[0], startPoint[1]);
this.ctx.lineTo(endPoint[0], endPoint[1]);
this.ctx.stroke();
}
绘制空心和填充的矩形只需要计算两个未点击的点:
drawHollowRectangle(startPoint, endPoint) {
this.ctx.beginPath();
this.ctx.strokeRect(
startPoint[0],
startPoint[1],
endPoint[0] - startPoint[0],
endPoint[1] - startPoint[1]
);
}drawFilledRectangle(startPoint, endPoint) {
this.ctx.beginPath();
this.ctx.fillRect(
startPoint[0],
startPoint[1],
endPoint[0] - startPoint[0],
endPoint[1] - startPoint[1]
);
}
绘制圆的方法需要使用距离公式计算半径,然后绘制 360 度圆弧。
drawHollowCircle(startPoint, endPoint) {
const x = startPoint[0] - endPoint[0];
const y = startPoint[1] - endPoint[1];
const radius = Math.sqrt(x * x + y * y);
this.ctx.beginPath();
this.ctx.arc(startPoint[0], startPoint[1], radius, 0, 2 * Math.PI, false);
this.ctx.stroke();
}drawFilledCircle(startPoint, endPoint) {
const x = startPoint[0] - endPoint[0];
const y = startPoint[1] - endPoint[1];
const radius = Math.sqrt(x * x + y * y);
this.ctx.beginPath();
this.ctx.arc(startPoint[0], startPoint[1], radius, 0, 2 * Math.PI, false);
this.ctx.fill();
}
附加 Canvas 事件侦听器
让画布真正响应被点击需要将这两行添加到构造函数中:
this.handleDraw = this.handleDraw.bind(this);
this.canvas.addEventListener('click', this.handleDraw);
那些使用过 React 的人可能会认识到这种模式。 但为什么需要它? 它与事件侦听器的工作方式有关。 如果没有绑定,handleDraw 中的 this 上下文变量将指向被点击的 HTML 元素。 在这种情况下,画布元素。 但这意味着我们无法访问具有我们需要的方法的 Canvas 对象。 通过使用 bind,我们强制 this 引用对象。
把它们放在一起
在此步骤中更改的文件此时应如下所示:
<html>
<head>
<title>Collaborative Drawing App</title>
<style type="text/css">
canvas {
border: 1px solid black;
}
.palette {
border: 1px solid black;
display: inline-block;
margin: 2px;
height: 25px;
width: 25px;
}
</style>
</head>
<body>
<h1>Collaborative Drawing App</h1>
<div>
<canvas id="canvas" height="500px" width="500px"></canvas><br />
</div>
<div id="palette">
<div id="row-1">
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
</div>
<div id="row-2">
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
</div>
</div>
<div id="draw-methods">
<button onclick="canvas.setMode('Line')">Line</button>
<button onclick="canvas.setMode('Hollow Rectangle')">Hollow Rectangle</button>
<button onclick="canvas.setMode('Filled Rectangle')">Filled Rectangle</button>
<button onclick="canvas.setMode('Hollow Circle')">Hollow Circle</button>
<button onclick="canvas.setMode('Filled Circle')">Filled Circle</button>
</div>
<script type="text/javascript" src="./script.js"></script>
<script type="text/javascript">
const canvas = new Canvas();
const palette = new Palette();
palette.draw(canvas);
</script>
</body>
</html>
class Canvas {
constructor() {
this.canvas = document.querySelector('#canvas');
this.ctx = this.canvas.getContext('2d');
this.activeColor = '#000000';
this.startPoint = null;
this.endPoint = null;
this.pointMode = 'start';
this.mode = 'Line';
this.handleDraw = this.handleDraw.bind(this);
this.canvas.addEventListener('click', this.handleDraw);
}
setColor(color) {
this.activeColor = color;
this.ctx.strokeStyle = color;
this.ctx.fillStyle = color;
}
setMode(mode) {
this.mode = mode;
}
handleDraw(e) {
const rect = this.canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
if (this.pointMode == 'start') {
this.startPoint = [x, y];
this.pointMode = 'end';
} else if (this.pointMode == 'end') {
this.pointMode = 'start';
this.endPoint = [x, y];
// do the drawing
if (this.mode == 'Line') {
this.drawLine(this.startPoint, this.endPoint);
} else if (this.mode == 'Hollow Rectangle') {
this.drawHollowRectangle(this.startPoint, this.endPoint);
} else if (this.mode == 'Filled Rectangle') {
this.drawFilledRectangle(this.startPoint, this.endPoint);
} else if (this.mode == 'Hollow Circle') {
this.drawHollowCircle(this.startPoint, this.endPoint);
} else if (this.mode == 'Filled Circle') {
this.drawFilledCircle(this.startPoint, this.endPoint);
}
this.startPoint = null;
this.endPoint = null;
}
}
drawLine(startPoint, endPoint) {
this.ctx.beginPath();
this.ctx.moveTo(startPoint[0], startPoint[1]);
this.ctx.lineTo(endPoint[0], endPoint[1]);
this.ctx.stroke();
}
drawHollowRectangle(startPoint, endPoint) {
this.ctx.beginPath();
this.ctx.strokeRect(
startPoint[0],
startPoint[1],
endPoint[0] - startPoint[0],
endPoint[1] - startPoint[1]
);
}
drawFilledRectangle(startPoint, endPoint) {
this.ctx.beginPath();
this.ctx.fillRect(
startPoint[0],
startPoint[1],
endPoint[0] - startPoint[0],
endPoint[1] - startPoint[1]
);
}
drawHollowCircle(startPoint, endPoint) {
const x = startPoint[0] - endPoint[0];
const y = startPoint[1] - endPoint[1];
const radius = Math.sqrt(x * x + y * y);
this.ctx.beginPath();
this.ctx.arc(startPoint[0], startPoint[1], radius, 0, 2 * Math.PI, false);
this.ctx.stroke();
}
drawFilledCircle(startPoint, endPoint) {
const x = startPoint[0] - endPoint[0];
const y = startPoint[1] - endPoint[1];
const radius = Math.sqrt(x * x + y * y);
this.ctx.beginPath();
this.ctx.arc(startPoint[0], startPoint[1], radius, 0, 2 * Math.PI, false);
this.ctx.fill();
}
}
class Palette {
constructor() {
this.colors = [
['#000000', '#FFFFFF', '#7F7F7F', '#C3C3C3', '#880015', '#B97A57', '#ED1C24', '#FFAEC9', '#FF7F27', '#FFC90E'],
['#FFF200', '#EFE4B0', '#22B14C', '#B5E61D', '#00A2E8', '#99D9EA', '#3F48CC', '#7092BE', '#A349A4', '#C8BFE7']
];
}
draw(canvas) {
const row1 = document.querySelectorAll('#row-1 .palette');
const row2 = document.querySelectorAll('#row-2 .palette');
row1.forEach((div, idx) => {
div.style.backgroundColor = this.colors[0][idx];
div.onclick = e => canvas.setColor(this.colors[0][idx]);
});
row2.forEach((div, idx) => {
div.style.backgroundColor = this.colors[1][idx];
div.onclick = e => canvas.setColor(this.colors[1][idx]);
});
}
}
重新启动节点服务器后,尝试更改颜色,设置要绘制的形状,然后开始单击画布。 这是我为确保它有效而进行的一些测试:
在下一篇文章中,我们将添加一些方便的函数来显示选择的形状,以及应用程序期望用户下一步做什么的简短说明。
tml5经历了前期html快速的更新换代,以其独有特性的优势迅速占据了网页开发市场鳌头。介于目前学习和想要从事html5网页开发的人越来越多。千锋老师给大家整理了一下基本的html5学习计划路线图,适应于一些零基础学习html5的同学借鉴。
HTML5学习路线规划:
一、HTML5基础
HTML 快速入门、文本、图像、链接、表格、列表、表单、框架;
二、CSS3基础
CSS基础语法、各种选择器(通用选择器、元素选择器、id和class选择器、后代选择器、伪类选择器等)、框模型与背景、文本格式化、表格、显示与定位、浏览器调试
三、HTML5高级
HTML5 增强表单元素、HTML5验证、HTML5新事件和新属性、Canvas绘图、HTML5 SVG、音频和视频处理、离线Web存储与应用、HTML5 拖放、Web Socket API、Geolocation API、Web Worker API
四、实战技能目标
掌握JQuery核心API,HTML5 中的绘图、音频视频处理、表单新特性,轻量级WEBAPP开发。
专业的html5开发工程师需要掌握的专业技术有:
第一阶段:前端页面重构:PC端网站布局、HTML5+CSS3基础项目、WebAPP页面布局;
第二阶段:JavaScript高级程序设计:原生JavaScript交互功能开发、面向对象开发与ES5/ES6、JavaScript工具库自主研发;
第三阶段:PC端全栈项目开发:jQuery经典特效交互开发、HTTP协议,Ajxa进阶与后端开发、前端工程化与模块化应用、PC端网站开发、PC端管理信息系统前端开发;
第四阶段:移动端webAPP开发:Touch端项目、微信场景项目、应用Vue.js开发WebApp项目、应用Ionic开发WebApp项目、应用React.js开发WebApp;
第五阶段:混合(Hybrid)开发:各类混合应用开发;
第六阶段:NodeJS全栈开发:WebApp后端系统开发;
第七阶段:大数据可视化:数据可视化入门、D3.jS详解及项目实战。
HTML5开发从根本上改变了开发者开发web和应用的方式,从桌面浏览器到移动应用,HTML5都已经成为前端开发必不可少的语言。特别是移动互联网的爆发和微信这个超级应用对HTML5的支持,掌握HTML5语言的程序员已然成为各个互联网公司的标配,薪资也是一路走高。
适的动画不仅更能吸引人们的眼球,也能让你的应用体验更为流畅,而将动画的效果做到极致,才能让用户感到使用你的应用是一种享受,而不是觉得生硬和枯燥。那么Web前端人员是否了解各种前端动画效果实现方式的异同,具体应用中又是如何实现的呢?下面就让我们一起来看一看吧~
一、JavaScript 动画
因为没有其它可用的实现方式,最初的前端动画都是JS来实现,实现上就是通过一个定时器setInterval 每隔一定时间来改变元素的样式,动画结束时clearInterval即可。早期的类库包括 jquery、prototype、mootools 等等都是这种方式。
尽管这种方式动画的可控性很强,但是问题也很明显:
· 性能不佳,因为需要不断获取和修改Dom的布局,所以导致了大量页面重排(repaint)
· 缺乏标准,不同的库使用了不同的API,导致即使是简单的动画也有各不相同的实现方式,调整起来比较耗时
· 带宽消耗,相对丰富的动画库代码量都很大,结果就是增加了http请求的大小,降低了页面的载入时间
二、CSS3 动画
css3 加了两种动画的实现方式,一种是 transition, 一种是 animation。
transition 包含4种属性:transition-delay transition-duration transition-property transition-timing-function,对应动画的4种属性: 延迟、持续时间、对应css属性和缓动函数,
transform 包含7种属性:animation-name animation-duration animation-timing-function animation-delay animation-direction animation-iteration-count animation-fill-mode animation-play-state,它们可以定义动画名称,持续时间,缓动函数,动画延迟,动画方向,重复次数,填充模式。
总的来书,css 动画相比与JS更轻量,性能更好,更易于实现,同时也不必担心缺乏标准和增加带宽消耗的问题。animation 相比 transtion 使用起来更为复杂,但也提供了更多的控制,其中最重要的就是 frame 的支持,不过通过一些简单的JS库,例如 TJ 的 move.js, 我们也能在JS中通过 transition 来实现更复杂的控制。
三、Html5 动画
Html5 定义了三种绘图的方式,canvas svg Webgl,其中svg做为一种可缩放矢量图形的实现是基于xml标签定义的,它有专门的 animate 标签来定义动画。而为 canvas 或者 Webgl 实现动画则需要通过 requestAnimationFrame (简称 raf) 来定期刷新画布。尽管说 raf 的方式会让代码变得复杂,但是因为不需要那么多的文档对象(通常浏览器只需要管理一个画布),它的性能也好很多,尤其是在内存吃紧的移动端上面。
通过新的 raf 接口以及一些改进手段我们也可以用JS来实现高性能的动画。主要手段如下:
1. 减少 Dom 样式属性查询,Dom 样式属性的查询会导致页面重排,从而消耗性能,通过将属性保存在JS变量中就可以避免在动画时去查询,从而减少卡顿。
2. 使用性能更好的 css transform 替代改变绝对定位元素的定位属性
3. 在移动设备上使用 3d 硬件加速,最简单办法就是添加 -Webkit-transform: translateZ(0),原因是移动端的显卡有很强的图形渲染能力,而每个应用的 WebvieW 内存却是极其有限的。
使用JS的动画可控性更好,比如说通过事件捕捉可以很容易的设定不同参数。这方面做的最全面的有 Velocity.js,它可做为jquery 插件使用,对于初学者很友好。加入465042726,关于前端方面的更多问题我们可以一起交流!
*请认真填写需求信息,我们会在24小时内与您取得联系。