思路剖析
在想到实现这个场景得时候,脑海中浮现的是先使用canvas,最终也确实是使用canvas完成的。所以这时候得去确定使用canvas的哪些api来完成。
canvas参考手册
我们主要会使用到drawImage,clearRect,fillText,measureText,toDataURL
new FileReader()
new Image();
基础代码实现
以下介绍基础代码
//用canvas绘制图片,以方便后续的添加水印操作
function init(imgSrc,option) {
// 水印属性
var img = new Image();
img.src = imgSrc;
img.setAttribute("crossOrigin",'Anonymous')
img.onload = function (e) {
width = e.path[0].naturalWidth;
height = e.path[0].naturalHeight;
// 动态创建一个canvas
let canvasList = document.getElementsByTagName('canvas')
if(!(canvasList && canvasList.length)){
canvasC = document.createElement("canvas");
canvasC.hidden = true
document.getElementsByTagName('body')[0].appendChild(canvasC)
}else{
canvasC = canvasList[0];
}
// 获取原画的长宽属性
canvasC.width = width;
canvasC.height = height;
let cxt = canvasC.getContext('2d');
cxt.clearRect(0,0,width,height)
cxt.drawImage(img, 0, 0);
// 水印属性添加。
let watermark = new Watermark(canvasC,option);
}
}
// 开始水印属性初始化
function Watermark(canvas,option) {
this.title = "默认水印";//水印内容
this.rotate = 0;//倾斜角度
this.color = 'rgba(250,250,250,1)';//水印颜色
this.size = '20';//水印大小
this.transparent = 1;//透明度
this.mode = 'rightB';//水印排布模式
this.overspread = '0';//是否循环铺满,有此属性的时候,modeList相关属性失效
option && this.getOption(option);
this.modeList = {
'leftT': {
top:this.size,
left:"10",
textAlign:"left"
},
'rightT': {
top:this.size,
left:width-10,
textAlign:"end"
},
'rightB': {
top:height-this.size,
left:width-10,
textAlign:"end"
},
'leftB': {
top:height-this.size,
left:"10",
textAlign:"left"
},
};// 排布模式组合
this.text(canvas)
}
// 开始绘制文字
Watermark.prototype.text = function(canvas){
let cxt = canvas.getContext('2d');
// 设置字体样式
cxt.font=`${this.size}px Georgia`;
// 设置颜色 和 设置透明度
cxt.fillStyle = this.color;
cxt.globalAlpha = this.transparent;
// 设置旋转角度
cxt.rotate(-this.rotate/180);
// 填充文案
textPosition = this.modeList[this.mode];
// 是否开启循环铺满
if(this.overspread == 1){
let orgTitle = this.title+' ';
let textHeight = -(-20 - this.size);//定义一个文字的高度
// 获取文字长度,然后先每排铺满
let textNum = Math.ceil(width/cxt.measureText(orgTitle).width)*2;
let heightNum = Math.ceil(height/textHeight);
this.title = new Array(textNum).join(orgTitle);
let leftF = 0,rightF = 0;
if(this.rotate>0){
leftF = height*Math.sin(this.rotate/180)
}else{
rightF = cxt.measureText(this.title).width*Math.sin(this.rotate/180)
}
// 开始循环
for(let i=0;i<heightNum*3;i++){
cxt.fillText(this.title,-leftF,i*textHeight+rightF);
}
}else{
cxt.textAlign = textPosition.textAlign;
cxt.fillText(this.title,textPosition.left,textPosition.top);
}
cxt.save();
this.imageUrl()
}
// 获取图片路径
Watermark.prototype.imageUrl = function(){
let dataURL = canvasC.toDataURL('image/png');
window.watermarkPreview.src = dataURL;
}
锦上添花
完成基础代码实现之后,我们会考虑我们的原图不使用固定的图片而是使用用户自主上传的图片来实现。于是我们能得到一个上传图片模块。
<input type="file" name="fileUpload" id="fileUpload" onchange="changeFile(this)" accept="image/">
// 上传图片
function changeFile(e){
let imgPreview = document.getElementById("preview");
let file = e.files && e.files[0];// 获取上传图片内容
if(file){
// 获取文件信息之后,转化成base64链接展示
let reader = new FileReader();
reader.readAsDataURL(file);//发起异步
reader.onload = function (res){
imgPreview.src = this.result;
orgImg = true;
}
}
}
这样我们就能使用用户上传的图片,给了用户更多的开放性。
对于水印的属性,我们也可以提供给用户来设置,比如水印文字,水印放置位置,水印大小,水印颜色。由于现在只支持文字水印,后续会考虑图片水印的制作。
添加用户设置水印属性模块,
<form action="" class="tool" id="toolForm">
<p>设置区域</p>
<!-- 上传功能,根据用户上传的图片进行动态设置,可用性提升 -->
<span>待处理图片:</span><input type="file" name="fileUpload" id="fileUpload" onchange="changeFile(this)" accept="image/"><br>
<!-- 动态输入水印文字 -->
<span>输入水印文字:</span><input name="title" type="text" maxlength="10" placeholder="输入水印文字"><br>
<span>输入文字大小:</span><input name="size" type="number" min="10" max="50" placeholder="输入文字大小"><br>
<!-- 颜色选择 -->
<span>选择字体颜色:</span><input name="color" type="text" min="15" max="50" placeholder="输入文字颜色"><br>
<span>选择字体透明度:</span><input name="transparent" type="number" min="0.1" max="1" placeholder="输入范围0.1~1"><br>
<span>选择水印旋转度:</span><input name="rotate" type="number" min="0" max="360" placeholder="输入旋转角度"><br>
<!-- 模式选择 -->
<span>选择水印模式:</span>
<select name="mode">
<option value="">请选择水印模式</option>
<option value="leftT">左上</option>
<option value="rightT">右上</option>
<option value="rightB">右下</option>
<option value="leftB">左下</option>
</select><br>
<span>水印是否铺满:</span>
<select name="overspread">
<option value="">水印是否铺满:</option>
<option value='1'>是</option>
<option value='0'>否</option>
</select><br>
<!-- 提交按钮 -->
<button onclick="setWatermark('toolForm')" type="button">保存设置</button>
</form>
/**
* @description 提交水印设置数据
* */
function setWatermark(formId){
if(!orgImg) return alert("请上传源图");
let formElm = document.getElementById(formId)
let inputList = formElm.getElementsByTagName("input");
let selectList = formElm.getElementsByTagName("select");
let option = {};
for(item of inputList){
option[item.name] = item.value
}
for(item of selectList){
option[item.name] = item.value
}
let imgPreview = document.getElementById("preview");
init(imgPreview.src,option)
}
到此,一个用户可以自定义水印,可以自己上传源图的水印功能就先到此。