предисловие
В этой статье в основном представлены:
- Введение в проект
- Отображение эффекта проекта
- Шаг за шагом для достижения эффекта проекта
- Ступай на яму
1. Введение проекта
имя:умная доска для рисования
Стек технологий:HTML5, CSS3, JavaScript, мобильный
Описание функции:
- Поддержка функции онлайн-рисования на ПК и мобильных устройствах.
- Реализуйте функции рисования, такие как произвольный выбор цвета кисти, регулировка толщины кисти и ластик.
- Реализуйте функцию локального сохранения онлайн-чертежной доски
- Поддержка операций отмены и возврата
-
пользовательский цвет фона[Эта функция еще не доведена до совершенства] Примечание. Этот проект является лишь небольшим проектом для работы с холстом и JavaScript. Есть проблема, которая еще не решена. Ластик стер фоновый слой. Я надеюсь, что кто-то может дать мне несколько советов, спасибо!
Во-вторых, отображение эффекта проекта
адрес проекта адрес предварительного просмотра
предварительный просмотр
Предварительный просмотр на ПК:
Мобильный предварительный просмотр:
После прочтения предварительного просмотра выше и опытаумная доска для рисованияЕсли вы думаете, что все в порядке, не забудьте поставить лайк, неважно, если вы очень взволнованы, я все равно очень взволнован. В конце концов, я очень горжусь эффектом проекта, которого я добился. Наговорив много чепухи, я можете начать сбивать код для достижения желаемого эффекта! ! !
Примечание. Следующая реализация эффекта проекта в основном относится к JavaScript, следующее предоставляется толькокод для реализации идей,не весь код.
Три, шаг за шагом к достижению эффекта проекта
(1) Страница анализа
пройти черездиаграмма вариантов использования, мы знаем, какие функции есть у пользователей, когда они заходят на наш сайт?
Что может сделать пользователь:
- рисовать
- Измените толщину кисти
- Переключить цвет кисти
- Используйте ластик, чтобы стереть ненужные части
- очистить монтажную область
- Сохраняйте свои рисунки как картинки
- Выполнение операций отмены и повтора
Переключить цвет фона монтажной области- Совместимость с мобильным терминалом (поддержка сенсорного экрана)
(2) макет HTML
При написании html я представил файлы css и файлы js.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>智绘画板</title>
<link rel="shortcut icon" href="./image/favicon.png" type="image/x-icon">
<link rel="stylesheet" href="./css/style.css">
</head>
<body>
<canvas id="canvas"></canvas>
<!-- 自定义背景颜色功能尚未完善好 -->
<!--<div class="bg-btn"></div>
<div class="color-group" id="bgGroup">
<h3>选择背景颜色:</h3>
<ul class="clearfix">
<li class="bgcolor-item" style="background-color: blue;"></li>
<li class="bgcolor-item" style="background-color: black;"></li>
<li class="bgcolor-item" style="background-color: #FF3333;"></li>
<li class="bgcolor-item" style="background-color: #0066FF;"></li>
<li class="bgcolor-item" style="background-color: #FFFF33;"></li>
<li class="bgcolor-item" style="background-color: #33CC66;"></li>
<li class="bgcolor-item" style="background-color: gray;"></li>
<li class="bgcolor-item" style="background-color: #F34334;"></li>
<li class="bgcolor-item" style="background-color: #fff;box-shadow: 0 1px 2px 0 rgba(32,33,36,0.28);"></li>
<li class="bgcolor-item" style="background-color: #9B27AC;"></li>
<li class="bgcolor-item" style="background-color: #4CB050;"></li>
<li class="bgcolor-item" style="background-color: #029688;"></li>
</ul>
<i class="closeBtn"></i>
</div>-->
<div class="tools">
<div class="container">
<button class="save" id="save" title="保存"></button>
<button class="brush active" id="brush" title="画笔"></button>
<button class="eraser" id="eraser" title="橡皮擦"></button>
<button class="clear" id="clear" title="清屏"></button>
<button class="undo" id="undo" title="撤销"></button>
<button class="redo" id="redo" title="再做"></button>
</div>
</div>
<div class="pen-detail" id="penDetail">
<i class="closeBtn"></i>
<p>笔大小</p>
<span class="circle-box"><i id="thickness"></i></span> <input type="range" id="range1" min="1" max="10" value="1">
<p>笔颜色</p>
<ul class="pen-color clearfix">
<li class="color-item active" style="background-color: black;"></li>
<li class="color-item" style="background-color: #FF3333;"></li>
<li class="color-item" style="background-color: #99CC00;"></li>
<li class="color-item" style="background-color: #0066FF;"></li>
<li class="color-item" style="background-color: #FFFF33;"></li>
<li class="color-item" style="background-color: #33CC66;"></li>
</ul>
<p>不透明度</p>
<i class="showOpacity"></i> <input type="range" id="range2" min="1" max="10" value="1">
</div>
<script src="./js/main.js"></script>
</body>
</html>
(3) Используйте CSS для украшения интерфейса
Код css может украсить интерфейс в соответствии с личными привычками, поэтому я не буду писать здесь код css, вы можете прочитать его непосредственнокод проектаИли смотрите из Inspect Element в инструментах разработчика. Если у вас есть какие-либо вопросы, вы можете поговорить со мной в частном порядке, я не думаю, что это большая проблема.
(4) Используйте JS для реализации конкретных функций проекта.
1. Подготовка
Первым делом подготовьте контейнер,то есть чертежную доску.В предыдущем html этот контейнер уже написан.Это чистый бред.
<canvas id="canvas"></canvas>
затем инициализируйте js
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
Я планирую сделать артборд полноэкранным, поэтому далее задаю ширину и высоту холста.
let pageWidth = document.documentElement.clientWidth;
let pageHeight = document.documentElement.clientHeight;
canvas.width = pageWidth;
canvas.height = pageHeight;
Поскольку некоторые IE не поддерживают холст, если мы хотим быть совместимыми с IE, мы можем создать холст, а затем использоватьexcanvas
Инициализировать, добавить для IEexCanvas.js, здесь мы явно не рассматриваем IE.
Но когда я меняю окно браузера на компьютере, артборд не будет адаптивно масштабироваться. Решение:
// 记得要执行autoSetSize这个函数哦
function autoSetSize(){
canvasSetSize();
// 当执行这个函数的时候,会先设置canvas的宽高
function canvasSetSize(){
// 把变化之前的画布内容copy一份,然后重新画到画布上
let imgData = context.getImageData(0,0,canvas.width,canvas.height);
let pageWidth = document.documentElement.clientWidth;
let pageHeight = document.documentElement.clientHeight;
canvas.width = pageWidth;
canvas.height = pageHeight;
context.putImageData(imgData,0,0);
}
// 在窗口大小改变之后,就会触发resize事件,重新设置canvas的宽高
window.onresize = function(){
canvasSetSize();
}
}
2. Реализовать функцию рисования
Идея реализации: слушать события мыши, использоватьdrawLine()
Метод отображает записанные данные.
- Инициализировать состояние кисти текущей монтажной области,
painting = false
. - При нажатии мышки (
mousedown
),Пучокpainting
установить какtrue
, указывая на то, что рисование продолжается, а мышь не отпущена. Запись щелчков мыши. - При нажатии мышь перемещается (
mousemove
)Сразузаписать точкуСпускайся и рисуй. - Если мышь двигается слишком быстро, браузер не успевает за скоростью отрисовки, и между точками будут промежутки, поэтому нам нужно соединить нарисованные точки линиями (
lineTo()
). - Когда мышь отпущена (
mouseup
),Пучокpainting
установить какfalse
.
Примечание:drawCircle
На самом деле, этот метод не нужно писать, это просто чтобы всем было понятно, с чего начать кликать?
function listenToUser() {
// 定义一个变量初始化画笔状态
let painting = false;
// 记录画笔最后一次的位置
let lastPoint = {x: undefined, y: undefined};
// 鼠标按下事件
canvas.onmousedown = function(e){
painting = true;
let x = e.clientX;
let y = e.clientY;
lastPoint = {'x':x,'y':y};
drawCircle(x,y,5);
}
// 鼠标移动事件
canvas.onmousemove = function(e){
if(painting){
let x = e.clientX;
let y = e.clientY;
let newPoint = {'x':x,'y':y};
drawLine(lastPoint.x, lastPoint.y, newPoint.x, newPoint.y);
lastPoint = newPoint;
}
}
// 鼠标松开事件
canvas.onmouseup = function(){
painting = false;
}
}
// 画点函数
function drawCircle(x,y,radius){
// 新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。
context.beginPath();
// 画一个以(x,y)为圆心的以radius为半径的圆弧(圆),
// 从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成。
context.arc(x,y,radius,0,Math.PI*2);
// 通过填充路径的内容区域生成实心的图形
context.fill();
// 闭合路径之后图形绘制命令又重新指向到上下文中。
context.closePath();
}
function drawLine(x1,y1,x2,y2){
// 设置线条宽度
context.lineWidth = 10;
// 设置线条末端样式。
context.lineCap = "round";
// 设定线条与线条间接合处的样式
context.lineJoin = "round";
// moveTo(x,y)将笔触移动到指定的坐标x以及y上
context.moveTo(x1,y1);
// lineTo(x, y) 绘制一条从当前位置到指定x以及y位置的直线
context.lineTo(x2,y2);
// 通过线条来绘制图形轮廓
context.stroke();
context.closePath();
}
3. Для достижения ластика
Идеи реализации:
- Получить элемент ластика
- Установите исходное состояние ластика,
eraserEnabled = false
. - монитор ластик
click
Событие, нажмите на ластик, измените состояние ластика,eraserEnabled = true
И класс переключения, внедрениеАктивированоЭффект. -
eraserEnabled
дляtrue
, переместите мышь сcontext.clearRect()
Ластик понял.
Но я обнаружил, что в API холста тот, который может очистить пиксель,clearRect
метод, ноclearRect
Чистая область метода прямоугольная.Ведь ластик в привычках большинства людей круглый, поэтому вводится мощная функция области клипа, т.е.clip
метод. Код ниже используетcontext.clearRect()
Ластик понял. Пожалуйста, смотрите шаг в разделе ямы, чтобы узнать, как лучше реализовать ластик.
let eraser = document.getElementById("eraser");
let eraserEnabled = false;
// 记得要执行listenToUser这个函数哦
function listenToUser() {
// ... 代表省略了之前写的代码
// ...
// 鼠标按下事件
canvas.onmousedown = function(e){
// ...
if(eraserEnabled){//要使用eraser
context.clearRect(x-5,y-5,10,10)
}else{
lastPoint = {'x':x,'y':y}
}
}
// 鼠标移动事件
canvas.onmousemove = function(e){
let x = e.clientX;
let y = e.clientY;
if(!painting){return}
if(eraserEnabled){
context.clearRect(x-5,y-5,10,10);
}else{
var newPoint = {'x':x,'y':y};
drawLine(lastPoint.x, lastPoint.y,newPoint.x, newPoint.y);
lastPoint = newPoint;
}
}
// ...
}
// 点击橡皮檫
eraser.onclick = function(){
eraserEnabled = true;
eraser.classList.add('active');
brush.classList.remove('active');
}
4. Реализовать функцию очистки экрана
Идеи реализации:
- Получите узел элемента.
- Нажмите кнопку «Очистить», чтобы очистить холст.
let reSetCanvas = document.getElementById("clear");
// 实现清屏
reSetCanvas.onclick = function(){
ctx.clearRect(0,0,canvas.width,canvas.height);
setCanvasBg('white');
}
// 重新设置canvas背景颜色
function setCanvasBg(color) {
ctx.fillStyle = color;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
5. Реализовать функцию сохранения в виде картинки
Идеи реализации:
- Получать
canvas.toDateURL
- Создайте и вставьте тег a на страницу
- тег href равен
canvas.toDateURL
и добавьте атрибут загрузки - Нажмите кнопку сохранения, сработает тег
click
мероприятие
let save = document.getElementById("save");
// 下载图片
save.onclick = function(){
let imgUrl = canvas.toDataURL('image/png');
let saveA = document.createElement('a');
document.body.appendChild(saveA);
saveA.href = imgUrl;
saveA.download = 'mypic'+(new Date).getTime();
saveA.target = '_blank';
saveA.click();
}
6. Реализовать функцию смены цвета фона
Идеи реализации:
Получите соответствующий узел элемента.Добавьте событие щелчка к каждой метке, класс которой — bgcolor-item, и измените цвет фона при запуске события щелчка.Щелкните за пределами div, где установлен цвет фона, чтобы скрыть этот div.
let selectBg = document.querySelector('.bg-btn');
let bgGroup = document.querySelector('.color-group');
let bgcolorBtn = document.querySelectorAll('.bgcolor-item');
let penDetail = document.getElementById("penDetail");
let activeBgColor = '#fff';
// 实现了切换背景颜色
for (let i = 0; i < bgcolorBtn.length; i++) {
bgcolorBtn[i].onclick = function (e) {
// 阻止冒泡
e.stopPropagation();
for (let i = 0; i < bgcolorBtn.length; i++) {
bgcolorBtn[i].classList.remove("active");
this.classList.add("active");
activeBgColor = this.style.backgroundColor;
setCanvasBg(activeBgColor);
}
}
}
document.onclick = function(){
bgGroup.classList.remove('active');
}
selectBg.onclick = function(e){
bgGroup.classList.add('active');
e.stopPropagation();
}
7. Реализовать функцию изменения толщины кисти
Идеи реализации:
- Реализация заставляет диалоговое окно установить свойства кисти.
- Получите соответствующий узел элемента.
- При изменении элемента input=range полученное значение присваивается lWidth.
- затем установите
context.lineWidth = lWidth
.
let range1 = document.getElementById('range1');
let lWidth = 2;
let ifPop = false;
range1.onchange = function(){
console.log(range1.value);
console.log(typeof range1.value)
thickness.style.transform = 'scale('+ (parseInt(range1.value)) +')';
console.log(thickness.style.transform )
lWidth = parseInt(range1.value*2);
}
// 画线函数
function drawLine(x1,y1,x2,y2){
// ...
context.lineWidth = lWidth;
// ...
}
// 点击画笔
brush.onclick = function(){
eraserEnabled = false;
brush.classList.add('active');
eraser.classList.remove('active');
if(!ifPop){
// 弹出框
console.log('弹一弹')
penDetail.classList.add('active');
}else{
penDetail.classList.remove('active');
}
ifPop = !ifPop;
}
8. Реализовать функцию смены цвета кисти
реализация идейИзменить цвет фона монтажной областиидеи похожи.
let aColorBtn = document.getElementsByClassName("color-item");
getColor();
function getColor(){
for (let i = 0; i < aColorBtn.length; i++) {
aColorBtn[i].onclick = function () {
for (let i = 0; i < aColorBtn.length; i++) {
aColorBtn[i].classList.remove("active");
this.classList.add("active");
activeColor = this.style.backgroundColor;
ctx.fillStyle = activeColor;
ctx.strokeStyle = activeColor;
}
}
}
}
9. Реализовать функцию смены отмены и повтора
Идеи реализации:
- Сохранить снимок: сохранить снимок холста в
canvasHistory
Массив (создание снимков с использованием холстаtoDataURL()
метод, который создает изображение base64); - Отзыв и антиотзыв: поставить
canvasHistory
Моментальный снимок соответствующего индекса в массиве использует холстdrawImage()
способ перерисовать его заново; - Нарисуйте новое изображение: при выполнении новой операции рисования удалите запись массива после текущей позиции и добавьте новый снимок.
let undo = document.getElementById("undo");
let redo = document.getElementById("redo");
// ...
canvas.ontouchend = function () {
painting = false;
canvasDraw();
}
// ...
canvas.onmouseup = function(){
painting = false;
canvasDraw();
}
let canvasHistory = [];
let step = -1;
// 绘制方法
function canvasDraw(){
step++;
if(step < canvasHistory.length){
canvasHistory.length = step; // 截断数组
}
// 添加新的绘制到历史记录
canvasHistory.push(canvas.toDataURL());
}
// 撤销方法
function canvasUndo(){
if(step > 0){
step--;
// ctx.clearRect(0,0,canvas.width,canvas.height);
let canvasPic = new Image();
canvasPic.src = canvasHistory[step];
canvasPic.onload = function () { ctx.drawImage(canvasPic, 0, 0); }
undo.classList.add('active');
}else{
undo.classList.remove('active');
alert('不能再继续撤销了');
}
}
// 重做方法
function canvasRedo(){
if(step < canvasHistory.length - 1){
step++;
let canvasPic = new Image();
canvasPic.src = canvasHistory[step];
canvasPic.onload = function () {
// ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(canvasPic, 0, 0);
}
redo.classList.add('active');
}else {
redo.classList.remove('active')
alert('已经是最新的记录了');
}
}
undo.onclick = function(){
canvasUndo();
}
redo.onclick = function(){
canvasRedo();
}
10. Совместимость с мобильными терминалами
Идеи реализации:
- Определите, поддерживает ли устройство сенсорный ввод.
-
true
, затем используйтеtouch
мероприятие;false
, затем используйтеmouse
мероприятие
// ...
if (document.body.ontouchstart !== undefined) {
// 使用touch事件
anvas.ontouchstart = function (e) {
// 开始触摸
}
canvas.ontouchmove = function (e) {
// 开始滑动
}
canvas.ontouchend = function () {
// 滑动结束
}
}else{
// 使用mouse事件
// ...
}
// ...
В-четвертых, наступив на яму
Вопрос 1: Сменить окно браузера на компьютере, артборд не адаптируется
Решение:
Определение обработки событий реагирования, доступ к параметру размера страницы изменяется параметры.
При изменении размера окна сбросить ширину и высоту холста.Короче говоря, после изменения окна переназначить canvas.width и canvas.height.
// 记得要执行autoSetSize这个函数哦
function autoSetSize(){
canvasSetSize();
// 当执行这个函数的时候,会先设置canvas的宽高
function canvasSetSize(){
let pageWidth = document.documentElement.clientWidth;
let pageHeight = document.documentElement.clientHeight;
canvas.width = pageWidth;
canvas.height = pageHeight;
}
// 在窗口大小改变之后,就会触发resize事件,重新设置canvas的宽高
window.onresize = function(){
canvasSetSize();
}
}
Вопрос 2: Это нормально, когда ширина нарисованной линии относительно мала, но когда она станет толще, возникнут проблемы.
Решение: Посмотрите документацию и получите метод, просто сделайте простую модификациюкод для рисования линийпросто хорошо
// 画线函数
function drawLine(x1,y1,x2,y2){
context.beginPath();
context.lineWidth = lWidth;
//-----加入-----
// 设置线条末端样式。
context.lineCap = "round";
// 设定线条与线条间接合处的样式
context.lineJoin = "round";
//-----加入-----
context.moveTo(x1,y1);
context.lineTo(x2,y2);
context.stroke();
context.closePath();
}
Вопрос 3: Как реализовать круглый ластик?
Решение:
В API холста тот, который может очищать пиксели,clearRect
метод, ноclearRect
Чистая область метода прямоугольная.Ведь ластик в привычках большинства людей круглый, поэтому вводится мощная функция области клипа, т.е.clip
метод. Использование очень простое:
ctx.save()
ctx.beginPath()
ctx.arc(x2,y2,a,0,2*Math.PI);
ctx.clip()
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.restore();
Приведенный выше код реализует стирание круглой области, то есть сначала реализует круговой путь, затем использует этот путь в качестве области отсечения, а затем очищает пиксели. Следует отметить, что вам нужно сначала сохранить среду рисования и сбросить среду рисования после очистки пикселей.Если вы не сбросите ее, будущие рисунки будут ограничены этой областью отсечения.
Вопрос 4: Как обеспечить совместимость с мобильными терминалами?
1. Добавьте метатеги
Поскольку браузер теперь инициирует страницу на телефоне, мы можем установить мета-атрибут области просмотра на вкладке «Мета», сообщая браузеру о масштабировании, ширина страницы = ширине экрана пользовательского оборудования.
<meta name="viewport" content="width=device-width,
initial-scale=1,user-scalable=no,
maximum-scale=1.0,minimum-scale=1.0"/>
/*
页面宽度=移动宽度 :width=device-width
用户不可以缩放:user-scalable=no
缩放比例:initial-scale=1
最大缩放比例:maximum-scale=1.0
最小缩放比例:minimum-scale=1.0
*/
2. Почти все сенсорные события используются на мобильной стороне, что отличается от стороны ПК.
Так как мобильный терминал является сенсорным событием, используются свойства touchstart/touchmove/touchend H5, но ПК-терминал поддерживает только события мыши, поэтому требуется определение функции.
существуетtouch
событие, через.touches[0].clientX
а также.touches[0].clientY
чтобы получить координаты, это то же самое, что иmouse
события выделяются.
Проблема 5: Canvas очищается при изменении размера браузера
Решение 1: http://js.jirengu.com/dafic/2/edit
Решение 2: http://js.jirengu.com/worus/2/edit
Ссылка на ссылку:При изменении длины и ширины холста содержимое холста исчезает.
Вопрос 6: Когда ластик движется быстро, он станет точкой
Ссылка на ссылку:HTML5 реализует эффект ластика ластика
Вопрос 7: Ластик стер фоновый слой, ластик нужно оптимизировать
Ну эта проблема пока не решена,поэтому сначала отменю функцию настройки цвета фона,но она бесполезна.Есть еще ластик который сотрет фоновый слой.Надеюсь кто то прочитает эту статью и дайте мне еще немного предложений и методов, спасибо!
Вопрос 8: Проблема в том, что после опустошения перекрашивается, а потом появляется оригинальная покраска
Ну, это не большая проблема, просто я не написал context.beginPath();, и я потратил немного времени на исправление ошибки, которая напомнила мне "десятки тысяч строк кода, прокомментируйте первую линия; программирование не стандартизировано, два коллеги, давайте плакать», по-прежнему хорошо работать в соответствии со спецификациями документа, это действительно ароматно! ! !
автор этой статьиxyyojl
Если в этой статье есть какие-либо ошибки, пожалуйста, оставьте сообщение, и я исправлю его вовремя.
Или вы можете сообщить об ошибках или запросах.
Если вы найдете это полезнымпоставить лайкилисобиратьБар!
Добро пожаловать на перепечатку или распространение, пожалуйста, указывайте источник при перепечатке