JS+Canvas实现自定义头像功能
本文介绍如何利用HTML5 Canvas和JavaScript实现自定义头像裁剪功能。通过监听鼠标事件实现拖拽与拉伸选区,结合html2canvas将DOM区域转换为图片,最终在多个Canvas容器中重绘出指定尺寸的头像。
写在最前
此前老虎官网的自定义头像功能基于Flash实现,未安装Flash插件的用户需手动允许其运行。因此,我接到任务使用Canvas重新实现相同功能。恰好近期正在研究Canvas技术,便欣然接受(即便未深入研究,想必也会接下这个任务,哈哈)。
成果展示

Git地址
https://github.com/ry928330/portraitDIY.git
功能说明
用户可通过拖拽左侧小方框,或将鼠标置于方框右下角点击并拉伸来调整选区。选区覆盖的图片部分将被自动截取,并在右侧多个容器中重新绘制显示。
用户可输入宽高以自定义头像尺寸,目前仅支持宽高相等的头像图片。
实现细节
由于需要对图片所在区域进行截图,需创建一个覆盖在图片DOM上的Canvas。这里提供一个函数,根据传入DOM元素的类名创建位置相同的Canvas,并覆盖在原DOM元素之上:
function createCanvasByClassName(tag) {
var canvasInitialWidth = $('.' + tag).width();
var canvasInitialHeight = $('.' + tag).height();
var left = $('.' + tag).offset().left - $('.' + tag).parent('.portraitContainer').offset().left + 1;
var top = $('.' + tag).offset().top - $('.' + tag).parent('.portraitContainer').offset().top + 1;
//var left = $('.' + tag).offset().left + 1;
//var top = $('.' + tag).offset().top + 1;
clearCanvasObj.left = $('.' + tag).offset().left + 1;
clearCanvasObj.top = $('.' + tag).offset().top + 1;
// clearCanvasObj.left = left;
// clearCanvasObj.top = top;
var canvasElement = $('<canvas></canvas>');
var randomNum = Math.floor(getRandom(0, 10000));
clearCanvasObj.canvasId = randomNum;
canvasElement.attr({
id: 'canvas',
width: canvasInitialWidth,
height: canvasInitialHeight
});
canvasElement.css({
position: 'absolute',
top: top,
left: left
});
//$('body').append(canvasElement);
var appendEle = $('.portraitContainer').append(canvasElement);
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
//ctx.fillStyle = "rgba(211,211,216,0.5)";
ctx.clearRect(0, 0, canvasInitialWidth, canvasInitialHeight);
ctx.fillStyle = "rgba(0,0,0, 0.4)";
ctx.fillRect(0, 0, canvasInitialWidth, canvasInitialHeight);
return canvas;
}拥有这张Canvas后,即可在图片区域进行操作。首先绘制浅黑色阴影,然后擦除初始小方框区域的颜色。接着为页面添加mousedown、mousemove和mouseup事件,实现类似拖拽的功能。重点在于mousemove事件的处理,代码如下:
function mousemoveFunc(event) {
/* Act on the event */
var nowMouseX = event.clientX - clearCanvasObj.left;
var nowMouseY = event.clientY - clearCanvasObj.top;
if (nowMouseX >= clearCanvasObj.xStart && nowMouseX <= clearCanvasObj.xStart + clearCanvasObj.width && nowMouseY >= clearCanvasObj.yStart && nowMouseY <= clearCanvasObj.yStart + clearCanvasObj.height) {
clearCanvasObj.isCanvasArea = true;
//clearCanvasObj.isRightCorner = false;
imgContainerCanvas.style.cursor = 'move';
} else if ((nowMouseX >= clearCanvasObj.xStart + clearCanvasObj.width - 10) && (nowMouseX <= clearCanvasObj.xStart+ clearCanvasObj.width + 10)
&& (nowMouseY >= clearCanvasObj.yStart + clearCanvasObj.height - 10) && (nowMouseY <= clearCanvasObj.yStart + clearCanvasObj.height + 10)) {
clearCanvasObj.isCanvasArea = true;
//clearCanvasObj.beginDraw = false;
imgContainerCanvas.style.cursor = 'se-resize';
}
else {
clearCanvasObj.isCanvasArea = false;
//clearCanvasObj.isRightCorner = false;
imgContainerCanvas.style.cursor = 'default';
}
var outerDomWidth = $(".imgContainer").width();
var outerDomHeight = $(".imgContainer").height();
var xDistance = event.clientX - clearCanvasObj.mouseX;
var yDistance = event.clientY - clearCanvasObj.mouseY;
//var outerCTX = canvas.getContext('2d');
//移动小方框
if (clearCanvasObj.beginDraw && clearCanvasObj.isCanvasArea && !clearCanvasObj.isRightCorner) {
ry_CTX.fillStyle = clearCanvasObj.color;
// console.log('1', clearCanvasObj.xStart, clearCanvasObj.yStart)
ry_CTX.fillRect(clearCanvasObj.xStart, clearCanvasObj.yStart, clearCanvasObj.width, clearCanvasObj.height);
//outerCTX.fillRect(0, 0, canvas.width, canvas.height);
clearCanvasObj.xStart += xDistance;
clearCanvasObj.yStart += yDistance;
//判断方框是否达到边界
if (clearCanvasObj.xStart <= 0) {
clearCanvasObj.xStart = 0;
}
if (clearCanvasObj.yStart <= 0) {
clearCanvasObj.yStart = 0;
}
if ((clearCanvasObj.xStart + clearCanvasObj.width) >= outerDomWidth) {
clearCanvasObj.xStart = outerDomWidth - clearCanvasObj.width;
}
if ((clearCanvasObj.yStart + clearCanvasObj.height) >= outerDomHeight) {
clearCanvasObj.yStart = outerDomHeight - clearCanvasObj.height;
}
// console.log('2', clearCanvasObj.xStart, clearCanvasObj.yStart)
ry_CTX.clearRect(clearCanvasObj.xStart, clearCanvasObj.yStart, clearCanvasObj.width, clearCanvasObj.height);
produceSmallPic(clearCanvasObj.xStart+clearCanvasObj.left, clearCanvasObj.yStart+clearCanvasObj.top, clearCanvasObj.width, clearCanvasObj.height, imageURL)
clearCanvasObj.mouseX = event.clientX;
clearCanvasObj.mouseY = event.clientY;
}
//拖拽小方框
if (clearCanvasObj.isRightCorner) {
ry_CTX.fillStyle = clearCanvasObj.color;
ry_CTX.fillRect(clearCanvasObj.xStart, clearCanvasObj.yStart, clearCanvasObj.width, clearCanvasObj.height);
var realDistance = Math.min(xDistance, yDistance)
clearCanvasObj.width += realDistance;
clearCanvasObj.height += realDistance;
//拖动时边界条件的判断
if (clearCanvasObj.xStart + clearCanvasObj.width >= outerDomWidth) {
clearCanvasObj.width = outerDomWidth - clearCanvasObj.xStart;
clearCanvasObj.height = outerDomWidth - clearCanvasObj.xStart;
}
if (clearCanvasObj.yStart + clearCanvasObj.height >= outerDomHeight) {
clearCanvasObj.width = outerDomHeight - clearCanvasObj.yStart;
clearCanvasObj.height = outerDomHeight - clearCanvasObj.yStart;
}
if (clearCanvasObj.width <= 10) {
clearCanvasObj.width = 10;
}
if (clearCanvasObj.height <= 10) {
clearCanvasObj.height = 10;
}
ry_CTX.clearRect(clearCanvasObj.xStart, clearCanvasObj.yStart, clearCanvasObj.width, clearCanvasObj.height);
produceSmallPic(clearCanvasObj.xStart+clearCanvasObj.left, clearCanvasObj.yStart+clearCanvasObj.top, clearCanvasObj.width, clearCanvasObj.height, imageURL);
clearCanvasObj.mouseX = event.clientX;
clearCanvasObj.mouseY = event.clientY;
}
}函数中需注意拖拽边界条件:一是方框不能移出图片所在的DOM区域;二是当鼠标位于小方框区域时,需改变鼠标样式。在拖动过程中,不断重绘方框移动区域(即绘制阴影),并在新位置调用clearRect函数擦除旧的小方框。在拖拽或拉伸时,持续调用produceSmallPic函数,根据右侧容器(每个均为Canvas)的大小重绘头像。代码如下:
function produceSmallPic(imageURL,left, top, width, height) {
var img = new Image();
img.src = imageURL;
var targetCtx = new Array();
var targetCanvas = null;
img.onload = function() {
portraitGroupsArr.forEach(function(item, index) {
targetCanvas = document.getElementById(item.class);
targetCtx.push(targetCanvas.getContext('2d'));
targetCtx[index].clearRect(0,0, item.width, item.height);
targetCtx[index].drawImage(img, left - clearCanvasObj.left, top - clearCanvasObj.top, width, height, 0, 0 , item.width, item.height);
})
}
}该函数的作用如下。需注意imageURL参数,它由图片所在的DOM转化而来。因为需要将DOM区域转化为图片以便使用drawImage截取所需部分,所以先利用html2canvas库将图片DOM转换为Canvas,获取包含截取图片的Canvas内容,再将其转化为图片并获取URL,代码如下:
html2canvas(document.getElementById('imgContainer'), {
onrendered: function(canvas) {
var imageURL = canvasTransToImage(canavs);
...
}
})
function canvasTransToImage(canvas) {
var imageURL = canvas.toDataURL('image/png');
return imageURL;
}随后,遍历右侧的Canvas容器,将图片重绘其中。整个过程至此结束,回顾来看逻辑较为清晰。
相关依赖
<script src="https://cdn.bootcss.com/html2canvas/0.5.0-beta4/html2canvas.min.js"></script>写在最后
Canvas操作需特别注意边界条件,明确何时重绘、何时清除是关键。只要逻辑清晰,Canvas API本身并不复杂,操作起来便不会过于繁琐。
到此这篇关于JS+Canvas实现自定义头像功能的文章就介绍到这了,更多相关JS Canvas自定义头像内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
JS监听滚动事件:回到顶部按钮的显示与隐藏逻辑
2026-11-05 16:19:37
JavaScript怎么读写LocalStorage与SessionStorage
2026-11-05 15:40:11
anime.js 与 GSAP 对比:轻量级项目该怎么选择动画库
2026-11-05 11:24:55
flv.js 与 hls.js 在直播场景下的选型对比
2026-11-05 08:18:52
JavaScript能做什么?前端开发中的作用与应用场景解析
2026-11-04 23:17:54
