這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)怎么在HTML5中使用Canvas實(shí)現(xiàn)一個(gè)放大鏡效果,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
公司主營(yíng)業(yè)務(wù):網(wǎng)站設(shè)計(jì)制作、做網(wǎng)站、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。創(chuàng)新互聯(lián)建站是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。創(chuàng)新互聯(lián)建站推出科爾沁免費(fèi)做網(wǎng)站回饋大家。原理
首先選擇圖片的一塊區(qū)域,然后將這塊區(qū)域放大,然后再繪制到原先的圖片上,保證兩塊區(qū)域的中心點(diǎn)一致, 如下圖所示:
undefined
初始化
獲得 canvas 和 image 對(duì)象,這里使用 標(biāo)簽預(yù)加載圖片, 關(guān)于圖片預(yù)加載可以看這里
var canvas = document.getElementById("canvas"); var context = canvas.getContext("2d"); var img = document.getElementById("img");
設(shè)置相關(guān)變量
// 圖片被放大區(qū)域的中心點(diǎn),也是放大鏡的中心點(diǎn) var centerPoint = {}; // 圖片被放大區(qū)域的半徑 var originalRadius = 100; // 圖片被放大區(qū)域 var originalRectangle = {}; // 放大倍數(shù) var scale = 2; // 放大后區(qū)域 var scaleGlassRectangle
畫背景圖片
function drawBackGround() { context.drawImage(img, 0, 0); }
計(jì)算圖片被放大的區(qū)域的范圍
這里我們使用鼠標(biāo)的位置作為被放大區(qū)域的中心點(diǎn)(放大鏡隨著鼠標(biāo)移動(dòng)而移動(dòng)),因?yàn)?canvas 在畫圖片的時(shí)候,需要知道左上角的坐標(biāo)以及區(qū)域的寬高,所以這里我們計(jì)算區(qū)域的范圍
function calOriginalRectangle(point) { originalRectangle.x = point.x - originalRadius; originalRectangle.y = point.y - originalRadius; originalRectangle.width = originalRadius * 2; originalRectangle.height = originalRadius * 2; }
繪制放大鏡區(qū)域
裁剪區(qū)域
放大鏡一般是圓形的,這里我們使用clip
函數(shù)裁剪出一個(gè)圓形區(qū)域,然后在該區(qū)域中繪制放大后的圖。一旦裁減了某個(gè)區(qū)域,以后所有的繪圖都會(huì)被限制的這個(gè)區(qū)域里,這里我們使用save
和restore
方法清除裁剪區(qū)域的影響。save
保存當(dāng)前畫布的一次狀態(tài),包含 canvas 的上下文屬性,例如style
,lineWidth
等,然后會(huì)將這個(gè)狀態(tài)壓入一個(gè)堆棧。restore
用來(lái)恢復(fù)上一次 save 的狀態(tài),從堆棧里彈出最頂層的狀態(tài)。
context.save(); context.beginPath(); context.arc(centerPoint.x, centerPoint.y, originalRadius, 0, Math.PI * 2, false); context.clip(); ...... context.restore();
計(jì)算放大鏡區(qū)域
通過(guò)中心點(diǎn)、被放大區(qū)域的寬高以及放大倍數(shù),獲得區(qū)域的左上角坐標(biāo)以及區(qū)域的寬高。
scaleGlassRectangle = { x: centerPoint.x - originalRectangle.width * scale / 2, y: centerPoint.y - originalRectangle.height * scale / 2, width: originalRectangle.width * scale, height: originalRectangle.height * scale }
繪制圖片
在這里我們使用context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
方法,將 canvas 自身作為一副圖片,然后取被放大區(qū)域的圖像,將其繪制到放大鏡區(qū)域里。
context.drawImage(canvas, originalRectangle.x, originalRectangle.y, originalRectangle.width, originalRectangle.height, scaleGlassRectangle.x, scaleGlassRectangle.y, scaleGlassRectangle.width, scaleGlassRectangle.height );
繪制放大邊緣
createRadialGradient
用來(lái)繪制漸變圖像
context.beginPath(); var gradient = context.createRadialGradient( centerPoint.x, centerPoint.y, originalRadius - 5, centerPoint.x, centerPoint.y, originalRadius); gradient.addColorStop(0, 'rgba(0,0,0,0.2)'); gradient.addColorStop(0.80, 'silver'); gradient.addColorStop(0.90, 'silver'); gradient.addColorStop(1.0, 'rgba(150,150,150,0.9)'); context.strokeStyle = gradient; context.lineWidth = 5; context.arc(centerPoint.x, centerPoint.y, originalRadius, 0, Math.PI * 2, false); context.stroke();
添加鼠標(biāo)事件
為 canvas 添加鼠標(biāo)移動(dòng)事件
canvas.onmousemove = function (e) { ...... }
轉(zhuǎn)換坐標(biāo)
鼠標(biāo)事件獲得坐標(biāo)一般為屏幕的或者 window 的坐標(biāo),我們需要將其裝換為 canvas 的坐標(biāo)。getBoundingClientRect
用于獲得頁(yè)面中某個(gè)元素的左,上,右和下分別相對(duì)瀏覽器視窗的位置。
function windowToCanvas(x, y) { var bbox = canvas.getBoundingClientRect(); return {x: x - bbox.left, y: y - bbox.top} }
修改鼠標(biāo)樣式
我們可以通過(guò) css 來(lái)修改鼠標(biāo)樣式
#canvas { display: block; border: 1px solid red; margin: 0 auto; cursor: crosshair; }
圖表放大鏡
我們可能基于 canvas 繪制一些圖表或者圖像,如果兩個(gè)元素的坐標(biāo)離得比較近,就會(huì)給元素的選擇帶來(lái)一些影響,例如我們畫兩條線,一個(gè)線的坐標(biāo)是(200.5, 400) -> (200.5, 200)
,另一個(gè)線的坐標(biāo)為(201.5, 400) -> (201.5, 20)
,那么這兩條線幾乎就會(huì)重疊在一起,如下圖所示:
使用圖表放大鏡的效果
在線演示 源碼
原理
類似于地圖中的圖例,放大鏡使用較為精確的圖例,如下圖所示:
在放大鏡坐標(biāo)系統(tǒng)中,原始的區(qū)域會(huì)變大,如下圖所示
繪制原始線段
首先創(chuàng)建一個(gè)線段對(duì)象
function Line(xStart, yStart, xEnd, yEnd, index, color) { // 起點(diǎn)x坐標(biāo) this.xStart = xStart; // 起點(diǎn)y坐標(biāo) this.yStart = yStart; // 終點(diǎn)x坐標(biāo) this.xEnd = xEnd; // 終點(diǎn)y坐標(biāo) this.yEnd = yEnd; // 用來(lái)標(biāo)記是哪條線段 this.index = index; // 線段顏色 this.color = color; }
初始化線段
// 原始線段 var chartLines = new Array(); // 處于放大鏡中的原始線段 var glassLines; // 放大后的線段 var scaleGlassLines; // 位于放大鏡中的線段數(shù)量 var glassLineSize; function initLines() { var line; line = new Line(200.5, 400, 200.5, 200, 0, "#888"); chartLines.push(line); line = new Line(201.5, 400, 201.5, 20, 1, "#888"); chartLines.push(line); glassLineSize = chartLines.length; glassLines = new Array(glassLineSize); for (var i = 0; i < glassLineSize; i++) { line = new Line(0, 0, 0, 0, i); glassLines[i] = line; } scaleGlassLines = new Array(glassLineSize); for (var i = 0; i < glassLineSize; i++) { line = new Line(0, 0, 0, 0, i); scaleGlassLines[i] = line; } }
繪制線段
function drawLines() { var line; context.lineWidth = 1; for (var i = 0; i < chartLines.length; i++) { line = chartLines[i]; context.beginPath(); context.strokeStyle = line.color; context.moveTo(line.xStart, line.yStart); context.lineTo(line.xEnd, line.yEnd); context.stroke(); } }
計(jì)算原始區(qū)域和放大鏡區(qū)域
function calGlassRectangle(point) { originalRectangle.x = point.x - originalRadius; originalRectangle.y = point.y - originalRadius; originalRectangle.width = originalRadius * 2; originalRectangle.height = originalRadius * 2; scaleGlassRectangle.width = originalRectangle.width * scale; scaleGlassRectangle.height = originalRectangle.height * scale; scaleGlassRectangle.x = originalRectangle.x + originalRectangle.width / 2 - scaleGlassRectangle.width / 2; scaleGlassRectangle.y = originalRectangle.y + originalRectangle.height / 2 - scaleGlassRectangle.height / 2; // 將值裝換為整數(shù) scaleGlassRectangle.width = parseInt(scaleGlassRectangle.width); scaleGlassRectangle.height = parseInt(scaleGlassRectangle.height); scaleGlassRectangle.x = parseInt(scaleGlassRectangle.x); scaleGlassRectangle.y = parseInt(scaleGlassRectangle.y); }
計(jì)算線段在新坐標(biāo)系統(tǒng)的位置
由原理圖我們知道,放大鏡中使用坐標(biāo)系的圖例要比原始坐標(biāo)系更加精確,比如原始坐標(biāo)系使用1:100
,那么放大鏡坐標(biāo)系使用1:10
,因此我們需要重新計(jì)算線段在放大鏡坐標(biāo)系中的位置。同時(shí)為了簡(jiǎn)便,我們將線段的原始坐標(biāo)進(jìn)行了轉(zhuǎn)化,減去原始區(qū)域起始的x值和y值,即將原始區(qū)域左上角的點(diǎn)看做為(0,0)
。
function calScaleLines() { var xStart = originalRectangle.x; var xEnd = originalRectangle.x + originalRectangle.width; var yStart = originalRectangle.y; var yEnd = originalRectangle.y + originalRectangle.height; var line, gLine, sgLine; var glassLineIndex = 0; for (var i = 0; i < chartLines.length; i++) { line = chartLines[i]; // 判斷線段是否在放大鏡中 if (line.xStart < xStart || line.xEnd > xEnd) { continue; } if (line.yEnd > yEnd || line.yStart < yStart) { continue; } gLine = glassLines[glassLineIndex]; sgLine = scaleGlassLines[glassLineIndex]; if (line.yEnd > yEnd) { gLine.yEnd = yEnd; } if (line.yStart < yStart) { gLine.yStart = yStart; } gLine.xStart = line.xStart - xStart; gLine.yStart = line.yStart - yStart; gLine.xEnd = line.xEnd - xStart; gLine.yEnd = line.yEnd - yStart; sgLine.xStart = parseInt(gLine.xStart * scale); sgLine.yStart = parseInt(gLine.yStart * scale); sgLine.xEnd = parseInt(gLine.xEnd * scale); sgLine.yEnd = parseInt(gLine.yEnd * scale); sgLine.color = line.color; glassLineIndex++; } glassLineSize = glassLineIndex; }
繪制放大鏡中心點(diǎn)
繪制放大鏡中心的瞄準(zhǔn)器
function drawAnchor() { context.beginPath(); context.lineWidth = 2; context.fillStyle = "#fff"; context.strokeStyle = "#000"; context.arc(parseInt(centerPoint.x), parseInt(centerPoint.y), 10, 0, Math.PI * 2, false); var radius = 15; context.moveTo(parseInt(centerPoint.x - radius), parseInt(centerPoint.y)); context.lineTo(parseInt(centerPoint.x + radius), parseInt(centerPoint.y)); context.moveTo(parseInt(centerPoint.x), parseInt(centerPoint.y - radius)); context.lineTo(parseInt(centerPoint.x), parseInt(centerPoint.y + radius)); //context.fill(); context.stroke(); }
繪制放大鏡
function drawMagnifyingGlass() { calScaleLines(); context.save(); context.beginPath(); context.arc(centerPoint.x, centerPoint.y, originalRadius, 0, Math.PI * 2, false); context.clip(); context.beginPath(); context.fillStyle = "#fff"; context.arc(centerPoint.x, centerPoint.y, originalRadius, 0, Math.PI * 2, false); context.fill(); context.lineWidth = 4; for (var i = 0; i < glassLineSize; i++) { context.beginPath(); context.strokeStyle = scaleGlassLines[i].color; context.moveTo(scaleGlassRectangle.x + scaleGlassLines[i].xStart, scaleGlassRectangle.y + scaleGlassLines[i].yStart); context.lineTo(scaleGlassRectangle.x + scaleGlassLines[i].xEnd, scaleGlassRectangle.y + scaleGlassLines[i].yEnd); context.stroke(); } context.restore(); context.beginPath(); var gradient = context.createRadialGradient( parseInt(centerPoint.x), parseInt(centerPoint.y), originalRadius - 5, parseInt(centerPoint.x), parseInt(centerPoint.y), originalRadius); gradient.addColorStop(0.50, 'silver'); gradient.addColorStop(0.90, 'silver'); gradient.addColorStop(1, 'black'); context.strokeStyle = gradient; context.lineWidth = 5; context.arc(parseInt(centerPoint.x), parseInt(centerPoint.y), originalRadius, 0, Math.PI * 2, false); context.stroke(); drawAnchor(); }
添加事件
鼠標(biāo)拖動(dòng)
鼠標(biāo)移動(dòng)到放大鏡上,然后按下鼠標(biāo)左鍵,可以拖動(dòng)放大鏡,不按鼠標(biāo)左鍵或者不在放大鏡區(qū)域都不可以拖動(dòng)放大鏡。
為了實(shí)現(xiàn)上面的效果,我們要實(shí)現(xiàn)3種事件mousedown
,mousemove
, 'mouseup', 當(dāng)鼠標(biāo)按下時(shí),檢測(cè)是否在放大鏡區(qū)域,如果在,設(shè)置放大鏡可以移動(dòng)。鼠標(biāo)移動(dòng)時(shí)更新放大鏡中興點(diǎn)的坐標(biāo)。鼠標(biāo)松開(kāi)時(shí),設(shè)置放大鏡不可以被移動(dòng)。
canvas.onmousedown = function (e) { var point = windowToCanvas(e.clientX, e.clientY); var x1, x2, y1, y2, dis; x1 = point.x; y1 = point.y; x2 = centerPoint.x; y2 = centerPoint.y; dis = Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2); if (dis < Math.pow(originalRadius, 2)) { lastPoint.x = point.x; lastPoint.y = point.y; moveGlass = true; } } canvas.onmousemove = function (e) { if (moveGlass) { var xDis, yDis; var point = windowToCanvas(e.clientX, e.clientY); xDis = point.x - lastPoint.x; yDis = point.y - lastPoint.y; centerPoint.x += xDis; centerPoint.y += yDis; lastPoint.x = point.x; lastPoint.y = point.y; draw(); } } canvas.onmouseup = function (e) { moveGlass = false; }
鼠標(biāo)雙擊
當(dāng)移動(dòng)到對(duì)應(yīng)的線段上時(shí),鼠標(biāo)雙擊可以選擇該線段,將該線段的顏色變?yōu)榧t色。
canvas.ondblclick = function (e) { var xStart, xEnd, yStart, yEnd; var clickPoint = {}; clickPoint.x = scaleGlassRectangle.x + scaleGlassRectangle.width / 2; clickPoint.y = scaleGlassRectangle.y + scaleGlassRectangle.height / 2; var index = -1; for (var i = 0; i < scaleGlassLines.length; i++) { var scaleLine = scaleGlassLines[i]; xStart = scaleGlassRectangle.x + scaleLine.xStart - 3; xEnd = scaleGlassRectangle.x + scaleLine.xStart + 3; yStart = scaleGlassRectangle.y + scaleLine.yStart; yEnd = scaleGlassRectangle.y + scaleLine.yEnd; if (clickPoint.x > xStart && clickPoint.x < xEnd && clickPoint.y < yStart && clickPoint.y > yEnd) { scaleLine.color = "#f00"; index = scaleLine.index; break; } } for (var i = 0; i < chartLines.length; i++) { var line = chartLines[i]; if (line.index == index) { line.color = "#f00"; } else { line.color = "#888"; } } draw(); }
鍵盤事件
因?yàn)榫€段離得比較近,所以使用鼠標(biāo)移動(dòng)很難精確的選中線段,這里使用鍵盤的w
,a
,s
,d
來(lái)進(jìn)行精確移動(dòng)
document.onkeyup = function (e) { if (e.key == 'w') { centerPoint.y = intAdd(centerPoint.y, -0.2); } if (e.key == 'a') { centerPoint.x = intAdd(centerPoint.x, -0.2); } if (e.key == 's') { centerPoint.y = intAdd(centerPoint.y, 0.2); } if (e.key == 'd') { centerPoint.x = intAdd(centerPoint.x, 0.2); } draw(); }
上述就是小編為大家分享的怎么在HTML5中使用Canvas實(shí)現(xiàn)一個(gè)放大鏡效果了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。