最近项目有个需求根据后端提供的图片旋转角度在页面上显示正向的图片,要求宽度是固定的高度自适应并且保证图片不能变形,一开始采用的是img的形式,img旋转之后不但坐标会混乱处理着麻烦,而且90度和270度的图片旋转成正向还会有空白滚动条的问题,最后决定用canvas实现。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5yM2QWO5MmNjNDO1AjZyATM5EGMzQjM1UmMmlzMmhDZw8CX4IzLcdDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL5M3Lc9CX6MHc0RHaiojIsJye.png)
这里绘制的图片要刚好铺满整个canvas,所以canvas的宽高就是图片的宽高,图片的高度是要与宽等比例不能变形的,canvas的宽等于父容器的clientWidth,图片可能很高会有滚动条,根据图片原始宽和高(naturalWidth:图片的原始宽,naturalWidth:图片的原始高),根据宽高比 width/height = canvas.width/canvas.height 计算得出canvas.height ,canvas本身是无法实现滚动条效果,所以需要个它添加一个父容器。
HTML:
<div class="wrap">
<canvas id="canvas"></canvas>
</div>
CSS
<style>
.wrap{
width: 500px;
height: 300px;
overflow-y: scroll;
}
</style>
js部分:
首先获取父容器和canvas,生成canvas的绘图环境,通过new Image()对象生成一个图片对象实例,对象实例通过src赋值图片,之后一切操作都放到onload 函数里面,图片加载完成后进行,不在这里面进行就无法获取到图片的原始尺寸。获取原始尺寸后通过宽高比列计算出需要设置图片的高度,在这里也是canvas的高度。
图片旋转角度为0时:直接计算完成通过ctx.drawImage方法绘制即可。
<script>
let wrap = document.querySelector('.wrap')
let canvas = document.querySelector('#canvas');
let ctx = canvas.getContext('2d');
canvas.width = wrap.clientWidth;
const img = new Image();
img.src = 'img/bg.jpg';
img.onload = function(){
let width = this.naturalWidth;
let height = this.naturalHeight;
canvas.height = canvas.width * height / width;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
}
</script>
效果:
图片旋转角度为90和270度时:因为这里牵涉到旋转所以要先知道canvas默认旋转中心点是哪里。根据下面图片可以直接看到中心点在左上角。这里应为是旋转90和270所以图片的宽高是互换的。
这里的计算 canvas.height = canvas.width * height / width; 要改成 canvas.height = canvas.width * width / height;
默认中心点在左上角一转就抛出画布范围里,为了方便操作和调试先把旋转中心点拉到中心来,(如果这三维视图很清晰的话,也可以不拉它)。
ctx.translate(canvas.width * 0.5, canvas.height * 0.5);
这是拉到中心为旋转的效果,然后给它旋转放正,这里可以正270度和负90度都可以把图片放正,我选择正270度。
旋转后效果
可以看到图片角度没问题了,宽高再拉回来一半就可以了
const img = new Image();
img.src = 'img/bg1.jpg';
img.onload = function(){
let width = this.naturalWidth;
let height = this.naturalHeight;
canvas.height = canvas.width * width / height;
ctx.translate(canvas.width * 0.5, canvas.height * 0.5);
ctx.rotate(1.5 * Math.PI);
ctx.drawImage(img, - canvas.height / 2, - canvas.width / 2, canvas.height, canvas.width);
}
图片旋转角度为180时:和90度的操作一样,只不过宽高不会互换罢了。
const img = new Image();
img.src = 'img/bg2.jpg';
img.onload = function(){
let width = this.naturalWidth;
let height = this.naturalHeight;
canvas.height = canvas.width * height / width;
ctx.translate(canvas.width * 0.5, canvas.height * 0.5);
ctx.rotate(1 * Math.PI);
ctx.drawImage(img, - canvas.width / 2, - canvas.height / 2, canvas.width, canvas.height);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
margin: 0;
padding: 0;
}
.wrap{
width: 500px;
height: 300px;
overflow-y: scroll;
margin: 20px;
}
</style>
</head>
<div class="wrap">
<canvas id="canvas"></canvas>
</div>
<body>
<script>
let wrap = document.querySelector('.wrap')
let canvas = document.querySelector('#canvas');
let ctx = canvas.getContext('2d');
canvas.width = wrap.clientWidth;
const img = new Image();
img.src = 'img/bg1.jpg';
let angle = 3;//旋转角度 1:90度,2:180度,3:270
img.onload = function(){
let width = this.naturalWidth;
let height = this.naturalHeight;
if(angle === 0){
canvas.height = canvas.width * height / width;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
}else if(angle === 1 || angle === 3){
canvas.height = canvas.width * width / height;
ctx.translate(canvas.width * 0.5, canvas.height * 0.5);
angle === 1 ? ctx.rotate(0.5 * Math.PI) : ctx.rotate(1.5 * Math.PI);
ctx.drawImage(img, - canvas.height / 2, - canvas.width / 2, canvas.height, canvas.width);
}else{
canvas.height = canvas.width * height / width;
ctx.translate(canvas.width * 0.5, canvas.height * 0.5);
ctx.rotate(1 * Math.PI);
ctx.drawImage(img, - canvas.width / 2, - canvas.height / 2, canvas.width, canvas.height);
}
}
</script>
</body>
</html>