場(chǎng)景:
創(chuàng)建簡(jiǎn)易的繪圖工具剪决,實(shí)現(xiàn)控制畫布上的四個(gè)錨點(diǎn)的拖拽修改已經(jīng)繪制的曲線圖,通過(guò)注釋實(shí)時(shí)顯示錨點(diǎn)的具體實(shí)時(shí)坐標(biāo),并通過(guò)反色清楚的顯示控制點(diǎn)的位置
要求:
1. 創(chuàng)建一個(gè)512*512的canvas畫布
2. 在該畫布上任意創(chuàng)建四個(gè)錨點(diǎn),使用貝塞爾曲線進(jìn)行連接
3. 可在畫布上自由拖拽上面的錨點(diǎn)彬犯,從而實(shí)現(xiàn)貝塞爾曲線的聯(lián)動(dòng)宁仔,每個(gè)點(diǎn)都有一個(gè)跟隨點(diǎn)移動(dòng)的注釋(文字+箭頭),說(shuō)明該點(diǎn)的坐標(biāo)
4.在此基礎(chǔ)上斗忌,實(shí)現(xiàn)一個(gè)黑白漸變色的背景闯第,然后在錨點(diǎn)周圍20個(gè)像素內(nèi)逢渔,將錨點(diǎn)所處的背景作反色,突出錨點(diǎn)的顯示
思路整理:
明確:
貝塞爾曲線通過(guò)canvas繪制,canvas自帶貝塞爾曲線繪制方法 bezierCurveTo()
顯示錨點(diǎn)可以在canvas上覆蓋div 將錨點(diǎn)放在div中
錨點(diǎn)拖動(dòng),添加事件監(jiān)聽(tīng), 鼠標(biāo)按下 為被點(diǎn)擊的錨點(diǎn) 添加鼠標(biāo)移動(dòng)事件,松開(kāi) 注銷鼠標(biāo)移動(dòng)事件
拖動(dòng)錨點(diǎn)時(shí)要做什么?
修改注釋內(nèi)的坐標(biāo)
canvas根據(jù)錨點(diǎn)坐標(biāo)重繪貝塞爾曲線
錨點(diǎn)周圍20像素背景色取反
效果展示
代碼(隨手寫 可自行優(yōu)化完善)
<style>
* {
margin: 0;
padding: 0;
}
#myCanvas {
position: absolute;
}
span {
position: absolute;
width: 20px;
height: 20px;
background: #21e40f;
border: 20px solid #fff;
border-radius: 50%;
color: rgb(25, 6, 204);
text-align: center;
font-size: 14px;
top: 50px;
left: 50px;
}
#end {
top: 50px;
left: 350px;
}
#dot1 {
top: 350px;
left: 50px;
}
#dot2 {
top: 350px;
left: 350px;
}
i {
position: absolute;
display: block;
width: 60px;
height: 40px;
background: rgba(121, 212, 170, 0.3);
left: 30px;
top: 30px;
}
</style>
<body>
<!-- 要求:
1. 創(chuàng)建一個(gè)512*512的canvas畫布
2. 在該畫布上任意創(chuàng)建四個(gè)錨點(diǎn)乡括,使用貝塞爾曲線進(jìn)行連接
3. 可在畫布上自由拖拽上面的錨點(diǎn),從而實(shí)現(xiàn)貝塞爾曲線的聯(lián)動(dòng)智厌,每個(gè)點(diǎn)都有一個(gè)跟隨點(diǎn)移動(dòng)的注釋(文字+箭頭)诲泌,說(shuō)明該點(diǎn)的坐標(biāo)
4. 在此基礎(chǔ)上,實(shí)現(xiàn)一個(gè)黑白漸變色的背景铣鹏,然后在錨點(diǎn)周圍20個(gè)像素內(nèi)敷扫,將錨點(diǎn)所處的背景作反色,突出錨點(diǎn)的顯示 -->
<!-- 畫布 -->
<canvas id="myCanvas" width="512" height="512" style="border:1px solid #000000;">
</canvas>
<!-- 畫布遮罩層 用于錨點(diǎn)顯示控制 -->
<div id="box">
<!-- 四個(gè)錨點(diǎn) 開(kāi)始 結(jié)束 錨點(diǎn)1 錨點(diǎn)2-->
<span id="start">1 <i>1</i></span>
<span id="dot1">2 <i>2</i></span>
<span id="dot2">3 <i>3</i></span>
<span id="end">4 <i>4</i></span>
</div>
<script>
//畫布漸變等
var c, ctx, grd;
//四個(gè)錨點(diǎn)
var start, end, dot1, dot2, liArr;
//坐標(biāo)等
var x = 0;
var y = 0;
var l = 0;
var t = 0;
var isDown = false;
init();
function init() {
// 獲取錨點(diǎn)
start = document.querySelector('#start')
end = document.querySelector('#end')
dot1 = document.querySelector('#dot1')
dot2 = document.querySelector('#dot2')
//錨點(diǎn)文字說(shuō)明以及跟隨樣式
liArr = document.querySelectorAll('i');
//獲取畫布
c = document.getElementById("myCanvas");
ctx = c.getContext("2d");
// 創(chuàng)建漸變
grd = ctx.createLinearGradient(0, 0, 512, 0);
grd.addColorStop(0, "black");
grd.addColorStop(1, "white");
// 填充漸變
ctx.fillStyle = grd;
ctx.fillRect(0, 0, 512, 512);
start.addEventListener('mousedown', downHandler)
end.addEventListener('mousedown', downHandler)
dot1.addEventListener('mousedown', downHandler)
dot2.addEventListener('mousedown', downHandler)
start.addEventListener('mouseup', upHandler)
end.addEventListener('mouseup', upHandler)
dot1.addEventListener('mouseup', upHandler)
dot2.addEventListener('mouseup', upHandler)
ctx.beginPath();
drawLine()
ctx.stroke();
}
//鼠標(biāo)按下回調(diào)
function downHandler(e) {
e.stopPropagation();
// 獲取x坐標(biāo)和y坐標(biāo)
x = e.clientX;
y = e.clientY;
//獲取左部和頂部的偏移量
l = e.target.offsetLeft;
t = e.target.offsetTop;
//開(kāi)關(guān)打開(kāi)
isDown = true;
//設(shè)置樣式
e.target.style.cursor = 'move';
//添加移動(dòng)偵聽(tīng)
e.target.addEventListener('mousemove', moveHandler)
e.target.addEventListener('mouseup', moveHandler)
}
//拖拽回調(diào)
function moveHandler(e) {
if (isDown == false) {
return;
}
//獲取x和y
var nx = e.clientX;
var ny = e.clientY;
//計(jì)算移動(dòng)后的左偏移量和頂部的偏移量
var nl = nx - (x - l);
var nt = ny - (y - t);
e.target.style.left = nl + 'px';
e.target.style.top = nt + 'px';
ctx.beginPath();
drawLine();
ctx.stroke();
}
function upHandler(e) {
//注銷移動(dòng)偵聽(tīng)事件
e.target.removeEventListener('mousemove', moveHandler)
}
//繪制貝塞爾曲線 / 設(shè)置 文字 / 背景反色
function drawLine() {
//獲取錨點(diǎn)坐標(biāo)
let s1 = setNum(start.offsetLeft);
let s2 = setNum(start.offsetTop);
let e1 = setNum(end.offsetLeft);
let e2 = setNum(end.offsetTop);
let dd1 = setNum(dot1.offsetLeft);
let dd2 = setNum(dot1.offsetTop);
let dd3 = setNum(dot2.offsetLeft);
let dd4 = setNum(dot2.offsetTop);
ctx.clearRect(20, 20, 100, 50);
ctx.fillStyle = grd;
ctx.fillRect(0, 0, 512, 512);
ctx.moveTo(s1, s2)
ctx.bezierCurveTo(dd1, dd2, dd3, dd4, e1, e2);
//顯示更新錨點(diǎn)坐標(biāo)
liArr[0].innerText = `x=> ${s1} x=> ${s2}`;
liArr[1].innerText = `x=> ${dd1} x=> ${dd2}`;
liArr[2].innerText = `x=> ${dd3} x=> ${dd4}`
liArr[3].innerText = `x=> ${e1} x=> ${e2}`
//設(shè)置邊框顏色
setColor(start, s1, s2);
setColor(end, e1, e2);
setColor(dot1, dd1, dd2);
setColor(dot2, dd3, dd4);
}
//處理貝塞爾曲線坐標(biāo)點(diǎn), 與div 中心重合
function setNum(str) {
return parseInt(str) + 30
}
//設(shè)置背景色反色
function setColor(target, x, y) {
let col = ctx.getImageData(x, y, 1, 1).data;
for (let i = 0; i < col.length; i++) {
col[i] = 255 - col[i]
}
console.log(target, col);
target.style.border = `20px solid rgb(${col[0]},${col[1]},${col[2]})`
}
</script>
</body>