Appearance
图片裁剪&压缩
需求
- 1、用户上传头像后可以自定义裁剪
- 2、头像大小超过5M时需要进行压缩上传
裁剪实现方案
- 1、获取到用户需要上传的头像,通过
FileReader
转换base64 - 2、实例化一个
new Image()
,监听load事件 - 3、根据画布的大小,和图片本身实际大小计算缩放的比例
- 4、创建一个canvas对象,通过首先清空画布,通过drawImage把图片渲染在canvas上,从(0,0)坐标点
- 5、在画布容器中定义一个蒙版,当蒙版位置发生改变时,计算蒙版(x,y)到canvas(0,0)坐标点距离
- 6、通过canvas对象中的getImageData,加上(x,y)坐标点距离,获取被裁剪的图片信息
- 7、在创建一个canvas对象,调用putImageData,最后通过canvas把图片转换成base64、或者是Blob
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>图片裁剪</title>
</head>
<style>
.box {
position: relative;
}
#canvas {
border: 1px solid red;
box-sizing: border-box;
}
#img {
width: 100px;
height: 100px;
border: 1px solid red;
box-sizing: border-box;
}
.operation {
width: 100px;
height: 100px;
background: gold;
position: absolute;
opacity: 0.2;
left: 0;
top: 0;
}
</style>
<body>
<input type="file" id="upload" />
<div class="box">
<canvas id="canvas" width="300" height="300">
</canvas>
<div class="operation"></div>
<div>
<button onclick="zoomIn()">放大</button>
<button onclick="zoomOut()">缩小</button>
<button onclick="cropping()">裁剪</button>
</div>
</div>
<img id="img"></img>
</body>
<script>
const canvas = document.querySelector( "#canvas" );
const ctx = canvas.getContext( "2d" );
const upload = document.getElementById( "upload" );
const img = document.getElementById( "img" );
const operation = document.querySelector( ".operation" );
let base64Image;
let times = 1; //图片放大的倍数
const ACCEPT = [ "image/jpg", "image/png", "image/jpeg" ];
const MAXSIZE = 10 * 1024 * 1024;
const MAXSIZE_STR = "10MB";
// Base64
function convertImageToBase64 ( file ) {
return new Promise( ( resolve, reject ) => {
let reader = new FileReader();
reader.addEventListener( "load", ( e ) => {
const base64Image = e.target.result;
resolve( base64Image );
reader = null;
} );
reader.readAsDataURL( file );
} );
}
// 图片等比缩放
function scale ( maxW, maxH, orgW, orgH ) {
let w;
let h;
if ( orgW <= maxW && orgH <= maxH ) {
// 1.宽高未超过最大尺寸=>使用原始尺寸
w = orgW;
h = orgH;
} else if ( orgW > maxW && orgH > maxH ) {
// 2.宽高超过最大尺寸
let ratioH = orgH / maxH;
let ratioW = orgW / maxW;
if ( ratioH === ratioW ) {
// 2.1 缩放比例相等,宽高都需要缩放
w = orgW / ratioW;
h = orgH / ratioH;
} else if ( ratioH > ratioW ) {
// 2.2 高的缩放比大于宽的缩放比=>高等于最大高,宽需要按照比例缩放
h = maxH;
w = orgW / ratioH;
} else {
// 2.2 宽的缩放比大于高的缩放比=>宽等于最大宽,高需要按照比例缩放
w = maxW;
h = orgH / ratioW;
}
} else if ( orgW > maxW ) {
// 3.高未超,宽超过最大尺寸=>宽等于最大宽,高需要按照比例缩放
let ratio = orgW / maxW;
w = maxW;
h = parseInt( orgH / ratio );
} else {
// 4.宽未超,高超过最大尺寸=>高等于最大高,宽需要按照比例缩放
let ratio = orgH / maxH;
h = maxH;
w = parseInt( orgW / ratio );
}
return {
w,
h,
};
}
// 绘制图片
function draw ( base64Image, x = 0, y = 0 ) {
const image = new Image();
image.addEventListener( "load", function ( e ) {
let maxW = 300; // 最大画布宽
let maxH = 300; // 最大画布高
let { w, h } = scale(
maxW,
maxH,
image.naturalWidth,
image.naturalHeight
);
ctx.clearRect( 0, 0, maxW, maxH ); // 清空画布
ctx.drawImage( image, x, y, w * times, h * times ); // 绘制图片
} );
image.src = base64Image;
}
// 放大
function zoomIn () {
times += 0.1;
draw( base64Image );
}
// 缩小
function zoomOut () {
times -= 0.1;
draw( base64Image );
}
// 裁剪
function cropping () {
let x = parseInt( operation.style.left || 0 )
let y = parseInt( operation.style.top || 0 )
const imageData = ctx.getImageData( x, y, 100, 100 ); //获取头像数据
let clipCanvas = document.createElement( "canvas" );
clipCanvas.width = 100;
clipCanvas.height = 100;
const clipContext = clipCanvas.getContext( "2d" );
clipContext.putImageData( imageData, 0, 0 );
let dataUrl = clipCanvas.toDataURL();
img.setAttribute( 'src', dataUrl )
}
upload.addEventListener( "change", async ( e ) => {
const [ file ] = e.target.files;
if ( !file ) {
return;
}
const { type: fileType, size: fileSize } = file;
if ( !ACCEPT.includes( fileType ) ) {
alert( `不支持[${ fileType }]文件类型!` );
upload.value = "";
return;
}
if ( fileSize > MAXSIZE ) {
alert( `文件超出${ MAXSIZE_STR }!` );
upload.value = "";
return;
}
base64Image = await convertImageToBase64( file );
draw( base64Image );
} );
function move ( ele, callback ) {
let isMove = false;
let startX = 0;
let startY = 0;
// 按下
function startMove ( e ) {
console.log( 'mousedown' )
isMove = true;
let { left, top } = ele.getBoundingClientRect(); // 视窗的距离
startX = e.pageX - left;
startY = e.pageY - top;
console.log(startX)
// 移动
document.addEventListener( "mousemove", handleMove );
// 释放
document.addEventListener( "mouseup", handleMouseUp );
}
function handleMove ( e ) {
console.log( 'mousemove' )
if ( isMove ) {
console.log( 'mousemove' )
callback && callback( e.pageX - startX, e.pageY - startY )
}
}
function handleMouseUp ( e ) {
if ( isMove ) {
console.log( 'mouseup' )
isMove = false;
}
// 解绑事件
document.removeEventListener( "mousemove", handleMove );
document.removeEventListener( "mouseup", handleMouseUp );
}
ele.addEventListener( "mousedown", startMove );
}
move( canvas, ( x, y ) => {
draw(
base64Image,
x,
y
);
} )
move( operation, ( x, y ) => {
operation.style.left = x + 'px'
operation.style.top = y + 'px'
} )
</script>
</html>
图片压缩实现(简单版本)
- 1、获取到用户需要上传的头像,通过
FileReader
转换base64 - 2、实例化一个
new Image()
,监听load事件 - 3、创建一个canvas对象,通过首先清空画布,通过drawImage把图片渲染在canvas
- 4、最后 canvas.toDataURL("image/jpeg", 0.5)、或者toBlob