CSS代码
*{margin:0; padding: 0;user-select: none;}
body{overflow:hidden}
#drawing-board{background: white;cursor: crosshair;width: 100%;height: 100%;}
.tools{position: fixed;left:180px;top: 447px; width:500px;display: flex;justify-content: center;text-align: center}
.tools button{border-radius: 50%;width: 50px;height: 50px;border: 1px solid #eee;outline: none;cursor: pointer;box-sizing: border-box;margin: 0 10px;text-align: center;color:#ccc;line-height: 50px;box-shadow:0 0 8px rgba(0,0,0,0.1); transition: 0.3s;}
.tools button.active,.tools button:active{box-shadow: 0 0 15px #00CCFF; color:#00CCFF;}
.tools button i{font-size: 24px;}
.color-group{position:fixed;width: 30px;left: 30px;top:230px;transform: translate(0,-150px)}
.color-group ul{list-style: none;}
.color-group ul li{width: 30px;height: 30px;margin: 10px 0;border-radius: 50%;box-sizing: border-box;border:3px solid white;box-shadow: 0 0 8px rgba(0,0,0,0.2);cursor: pointer;transition: 0.3s;}
.color-group ul li.active{box-shadow:0 0 15px #00CCFF;}
#range-wrap{position: fixed;top: 22%;left:874px;width: 30px;height: 150px;margin-top: -75px;}
#range-wrap input{transform: rotate(-90deg);width: 150px;height: 20px;margin: 0;transform-origin: 75px 75px; border-radius: 15px;-webkit-appearance: none;outline: none;position: relative;}
#range-wrap input::after{display: block;content:"";width:0;height: 0;border:5px solid transparent;
border-right:150px solid #00CCFF;border-left-width:0;position: absolute;left: 0;top: 5px;border-radius:15px; z-index: 0; }
#range-wrap input[type=range]::-webkit-slider-thumb,#range-wrap input[type=range]::-moz-range-thumb{-webkit-appearance: none;}
#range-wrap input[type=range]::-webkit-slider-runnable-track,#range-wrap input[type=range]::-moz-range-track {height: 10px;border-radius: 10px;box-shadow: none;}
#range-wrap input[type=range]::-webkit-slider-thumb{-webkit-appearance: none;height: 20px;width: 20px;margin-top: -1px;background: #ffffff;border-radius: 50%;box-shadow: 0 0 8px #00CCFF;position: relative;z-index: 999;}
@media screen and (max-width: 768px) {
.tools{bottom:auto;top:20px;}
.tools button{width: 35px;height: 35px;line-height: 35px;margin-bottom: 15px;box-shadow:0 0 5px rgba(0,0,0,0.1);}
.tools button.active,.tools button:active{box-shadow: 0 0 5px #00CCFF;}
.tools button i{font-size: 18px;}
.tools #swatches{display: none}
.color-group{left: 0;top:auto;bottom: 20px;display: flex;width:100%;justify-content: center;text-align: center;transform: translate(0,0)}
.color-group ul li{display: inline-block;margin:0 5px;}
.color-group ul li.active{box-shadow:0 0 10px #00CCFF;}
#range-wrap{right:auto;left: 20px;}
}
#imgcontainer {
position: absolute;
z-index: 999;
top: 80%;
left: 50%;
transform: translate(-50%, -50%);
}
#imgcontainer li{
width:160px;
height: 80px;
list-style: none;
margin: 0 10px;
}
#imgcontainer ul{
display: flex;
justify-content: center;
}
#imgcontainer img{
width: 100%;
height: 100%;
}
#a{
height: 500px;
width: 900px;
position: relative;
display:none
}
html,body{
width: 100%;
height: 100%;
}
JS代码
let canvas = document.getElementById("drawing-board");
let ctx = canvas.getContext("2d");
let eraser = document.getElementById("eraser");
let brush = document.getElementById("brush");
let reSetCanvas = document.getElementById("clear");
let aColorBtn = document.getElementsByClassName("color-item");
let save = document.getElementById("save");
let undo = document.getElementById("undo");
let range = document.getElementById("range");
let clear = null;
let ioClear = null;
let activeColor = 'black';
let lWidth = 4;
let lWidthIo = 4;
var socket = io('192.168.0.133:8081', {transports: ['websocket']});
let ioColor = 'black';
var originalWidth = null;
var originalHeight = null;
// 监听连接
var flag = true;
var noSaveThePicture = null;
autoSetSize(canvas);
setCanvasBg('white');
listenToUser(canvas);
getColor();
// window.onbeforeunload = function(){
// return "Reload site?";
// };
function autoSetSize(canvas) {
canvasSetSize();
// window.onresize = function () {
// noSaveThePicture = ctx.getImageData(0,0,1920,973)
// canvasSetSize();
// }
}
function canvasSetSize() {
let pageWidth = document.getElementById('a').offsetWidth;
let pageHeight = document.getElementById('a').offsetHeight;
canvas.width = pageWidth;
originalWidth = pageWidth;
canvas.height = pageHeight;
originalHeight = pageHeight;
// if(noSaveThePicture != null){
// ctx.putImageData(noSaveThePicture,0,0,0,0,pageWidth,pageHeight)
// }
}
function setCanvasBg(color) {
ctx.fillStyle = color;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
}
function listenToUser(canvas) {
let painting = false;
let lastPoint = {x: undefined, y: undefined};
// if (document.body.ontouchstart !== undefined) {
// canvas.ontouchstart = function (e) {
// this.firstDot = ctx.getImageData(0, 0, canvas.width, canvas.height);//在这里储存绘图表面
// saveData(this.firstDot);
// painting = true;
// let x = e.touches[0].clientX;
// let y = e.touches[0].clientY;
// lastPoint = {"x": x, "y": y};
// ctx.save();
// drawCircle(x, y, 0);
// };
// canvas.ontouchmove = function (e) {
// if (painting) {
// let x = e.touches[0].clientX;
// let y = e.touches[0].clientY;
// let newPoint = {"x": x, "y": y};
// drawLine(lastPoint.x, lastPoint.y, newPoint.x, newPoint.y);
// lastPoint = newPoint;
// }
// };
// canvas.ontouchend = function () {
// painting = false;
// }
// } else {
canvas.onmousedown = function (e) {
this.firstDot = ctx.getImageData(0, 0, canvas.width, canvas.height);//在这里储存绘图表面
saveData(this.firstDot);
painting = true;
let x = e.clientX;
let y = e.clientY;
let canvasWidth = document.getElementById('a').offsetWidth;
let canvasHeight = document.getElementById('a').offsetHeight;
let conversionWidth = originalWidth/canvasWidth
let conversionHeight = originalHeight/canvasHeight
x = x*conversionWidth
y = y*conversionHeight
socket.emit('serverDown',{x,y,originalWidth,originalHeight})
lastPoint = {"x": x, "y": y};
ctx.save();
drawCircle(x, y, 0);
};
canvas.onmousemove = function (e) {
if (painting) {
let x = e.clientX;
let y = e.clientY;
let canvasWidth = document.getElementById('a').offsetWidth;
let canvasHeight = document.getElementById('a').offsetHeight;
let conversionWidth = originalWidth/canvasWidth
let conversionHeight = originalHeight/canvasHeight
x = x*conversionWidth
y = y*conversionHeight
socket.emit('serverMouse',{x,y,originalWidth,originalHeight})
let newPoint = {"x": x, "y": y};
drawLine(lastPoint.x, lastPoint.y, newPoint.x, newPoint.y,clear);
lastPoint = newPoint;
}
};
canvas.onmouseup = function () {
socket.emit('serverUp',false)
painting = false;
};
canvas.mouseleave = function () {
socket.emit('serverLeave',false)
painting = false;
}
// }
}
function drawCircle(x, y, radius) {
ctx.save();
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();
if (clear) {
ctx.clip();
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.restore();
}
ctx.stroke();
ctx.closePath();
}
function drawLine(x1, y1, x2, y2) {
ctx.lineWidth = lWidth;
ctx.lineCap = "round";
ctx.lineJoin = "round";
if (clear) {
ctx.beginPath()
ctx.save();
ctx.globalCompositeOperation = "destination-out";
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
ctx.closePath();
ctx.clip();
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.restore();
}else{
ctx.beginPath()
ctx.fillStyle = activeColor;
ctx.strokeStyle = activeColor;
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
ctx.closePath();
}
}
function drawLineIo(x1, y1, x2, y2,ioClear,ioColor) {
ctx.lineCap = "round";
ctx.lineJoin = "round";
if (ioClear) {
ctx.beginPath()
ctx.lineWidth = lWidthIo;
ctx.save();
ctx.globalCompositeOperation = "destination-out";
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
ctx.closePath();
ctx.clip();
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.restore();
}else{
ctx.beginPath()
ctx.lineWidth = lWidthIo;
ctx.fillStyle = ioColor;
ctx.strokeStyle = ioColor;
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
ctx.closePath();
}
}
range.onchange = function(){
lWidth = this.value;
socket.emit('serverLindeWidth',lWidth)
};
eraser.onclick = function () {
clear = true;
socket.emit('serverClear',true)
this.classList.add("active");
brush.classList.remove("active");
};
brush.onclick = function () {
clear = null;
socket.emit('serverClear',null)
this.classList.add("active");
eraser.classList.remove("active");
};
reSetCanvas.onclick = function () {
ctx.clearRect(0, 0, canvas.width, canvas.height);
setCanvasBg('white');
ctx.drawImage(originalityImg,0,0,canvas.width,canvas.height)
socket.emit('serverOriginalityImg','')
};
socket.on('clientOriginalityImg',function(){
ctx.clearRect(0, 0, canvas.width, canvas.height);
setCanvasBg('white');
ctx.drawImage(originalityImg,0,0,canvas.width,canvas.height)
})
save.onclick = function () {
let imgUrl = canvas.toDataURL("image/png");
let saveA = document.createElement("a");
document.body.appendChild(saveA);
saveA.href = imgUrl;
saveA.download = "zspic" + (new Date).getTime();
saveA.target = "_blank";
saveA.click();
};
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;
socket.emit('serverColor',activeColor)
ctx.fillStyle = activeColor;
ctx.strokeStyle = activeColor;
}
}
}
}
let historyDeta = [];
function saveData (data) {
(historyDeta.length === 10) && (historyDeta.shift());// 上限为储存10步,太多了怕挂掉
historyDeta.push(data);
}
undo.onclick = function(){
if(historyDeta.length < 1) return false;
ctx.putImageData(historyDeta[historyDeta.length - 1], 0, 0);
historyDeta.pop()
socket.emit('serverUndo','')
};
socket.on('clientUndo',function(){
if(historyDeta.length < 1) return false;
ctx.putImageData(historyDeta[historyDeta.length - 1], 0, 0);
historyDeta.pop()
})
socket.on('clientDown', function(data) {
canvas.firstDot = ctx.getImageData(0, 0, canvas.width, canvas.height);//在这里储存绘图表面
saveData(canvas.firstDot);
painting = true;
let x = data.x;
let y = data.y;
let canvasWidth = document.getElementById('a').offsetWidth; //获取当前窗口宽度
let canvasHeight = document.getElementById('a').offsetHeight;
let conversionWidth = canvasWidth/data.originalWidth //获取对方宽度换算比列
let conversionHeight = canvasHeight/data.originalHeight
let theirConversionWidth = originalWidth/canvasWidth //获取自身窗口换算比列
let theirConversionHeight = originalHeight/canvasHeight
x = x*conversionWidth*theirConversionWidth //转换后的x坐标
y = y*conversionHeight*theirConversionHeight
lastPointIo = {"x": x, "y": y};
ctx.save();
drawCircle(x, y, 0);
});
let lastPointIo = {x: undefined, y: undefined};
socket.on('clientMouse',function(data){
if (painting) {
let x = data.x;
let y = data.y;
let canvasWidth = document.getElementById('a').offsetWidth;
let canvasHeight = document.getElementById('a').offsetHeight;
let conversionWidth = canvasWidth/data.originalWidth
let conversionHeight = canvasHeight/data.originalHeight
let theirConversionWidth = originalWidth/canvasWidth
let theirConversionHeight = originalHeight/canvasHeight
x = x*conversionWidth*theirConversionWidth
y = y*conversionHeight*theirConversionHeight
let newPoint = {"x": x, "y": y};
drawLineIo(lastPointIo.x, lastPointIo.y, newPoint.x, newPoint.y,ioClear,ioColor);
lastPointIo = newPoint;
}
})
socket.on('clientColor',function(data){
ioColor = data
})
socket.on('clientClear',function(data){
ioClear= data
})
socket.on('clientLindeWidth',function(data){
lWidthIo = data
})
let originalityImg = document.getElementById('firstView');
let SaveThePicture = 0;
ctx.drawImage(document.getElementById('firstView'),0,0,canvas.width,canvas.height)
let imgcontainer = document.getElementById('imgcontainer')
let imgData = [null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]
imgcontainer.addEventListener('click',function(e){
if(e.target.nodeName == 'IMG'){
originalityImg = e.target
let imgNum = e.target.getAttribute('data-id')
imgData[SaveThePicture] = ctx.getImageData(0,0,canvas.width,canvas.height)
socket.emit('serverImg',imgNum,SaveThePicture)
SaveThePicture = imgNum
if(imgData[SaveThePicture] != null){
ctx.putImageData(imgData[SaveThePicture],0,0)
}else{
ctx.drawImage(e.target,0,0,canvas.width,canvas.height)
}
}
})
socket.on('clientImg',function(data,data2){
let imgItem = document.getElementsByTagName('img')
SaveThePicture = data2
let imgNum = imgItem[data].getAttribute('data-id')
originalityImg = imgItem[data]
imgData[SaveThePicture] = ctx.getImageData(0,0,canvas.width,canvas.height)
SaveThePicture = imgNum
if(imgData[imgNum] != null){
ctx.putImageData(imgData[imgNum],0,0)
}else{
ctx.drawImage(imgItem[data],0,0,canvas.width,canvas.height)
}
})
socketIo 代码
var app = require('express')();
var fs = require('fs')
var express = require("express");
var key = fs.readFileSync('./private.key') //HTTPS证书
var cert = fs.readFileSync('./mydomain.crt') //HTTPS证书
var options = {
key:key,
cert:cert
}
var server = require('https').Server(options,app);
// 引入 socket.io
var io = require('socket.io')(server);
// 监听 8081 端口
server.listen(8081);
// 开启静态资源服务
app.use(express.static("./public"));
app.get('/',function(req,res){
fs.readFile('./canvas.html',(err,data)=>{
if(err) throw err;
res.write(data)
res.end()
})
})
// io 各种事件
io.on('connection', function (socket) {
socket.emit('message', { hello: '欢迎链接' });
socket.on('serverDown', function (data) {
socket.broadcast.emit('clientDown', data);
});
socket.on('serverMouse', function (data) {
socket.broadcast.emit('clientMouse', data);
});
socket.on('serverUp', function (data) {
socket.broadcast.emit('clientUp', data);
});
socket.on('serverLeave', function (data) {
socket.broadcast.emit('clientLeave', data);
});
socket.on('serverColor', function (data) {
socket.broadcast.emit('clientColor', data);
});
socket.on('serverClear', function (data) {console.log(data)
socket.broadcast.emit('clientClear', data);
});
socket.on('serverImg', function (data,data2) {
socket.broadcast.emit('clientImg', data,data2);
});
socket.on('serverLindeWidth', function (data) {
socket.broadcast.emit('clientLindeWidth',data);
});
socket.on('serverOriginalityImg', function () {
socket.broadcast.emit('clientOriginalityImg','');
});
socket.on('serverUndo', function () {
socket.broadcast.emit('clientUndo','');
});
});