前言
前几天刷视频,看到了一个用box-shadow绘制图片的效果,心血来潮,想来看看自己能不能实现。自己尝试了一下如何去实现,下面就看看主要的流程是什么样吧
思考过程
- 上传图片拿到文件对象
- 读取文件内容
- 加载图片内容,将图片转换成base64
- 绘制到canvas中
- 获取canvas中的图片的像素点
- 绘制阴影字符串,并渲染dom节点
效果图
代码开始
这里我用的是vue3的代码实现的,不过逻辑都是类似的,可以用原生实现也很方便
基本结构
<div>
<div class="img-box" :style="{ width: imgboxWidth + 'px' }">
<img :src="imgUrl" alt="" />
</div>
<div>
<input type="file" name="file" @change="handleFileChange" />
</div>
<div class="boxShadow" ref="boxShadow"> </div>
<div><span>box-shadow</span></div>
</div>
<style lang="less">
.img-box {
width: 300px;
img {
width: 100%;
height: 100%;
}
}
.boxShadow {
width: 1px;
height: 1px;
}
</style>
逻辑代码
<script setup lang="ts">
import { ref } from 'vue'
const imgUrl = ref()
const boxShadow = ref()
// 默认上传图片容器高度
const imgboxWidth = ref(300)
// 等比例图片高度
const imgHeight = ref(0)
// 流程
// 文件对象 -> 读取文件内容 -> 显示图片 -> 绘制到canvas -> 获取像素点 -> 绘制阴影
const handleFileChange = (e: any) => {
const file = e.target.files[0]
// 读取文件内容
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => {
// 显示图片
imgUrl.value = reader.result as string
const img = new Image()
img.src = imgUrl.value
img.onload = () => {
// 获取图片高度
imgHeight.value = Math.floor(img.height * (imgboxWidth.value / img.width))
// 显示图片
createCanvas(img)
}
}
}
// 绘制canvas
const createCanvas = (img: any) => {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
// 图片容器的宽度
canvas.width = imgboxWidth.value
// 等比例图片高度
canvas.height = imgHeight.value
// 绘制图片到canvas
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
// 获取像素点
const canvasData = ctx.getImageData(0, 0, canvas.width, canvas.height)
let boxShadowStr = ''
// 阴影像素点
let x = 0
let y = 0
const maxLen = canvasData.data.length
for (let i = 0; i < maxLen; i += 4) {
boxShadowStr += `${x}px ${y}px rgb(${canvasData.data[i]}, ${canvasData.data[i + 1]}, ${canvasData.data[i + 2]}),`
x++
// 换行
if (x >= canvas.width) {
x = 0
y++
}
}
// 绘制阴影
boxShadow.value.style.boxShadow = boxShadowStr.slice(0, boxShadowStr.length - 1)
}
</script>
总结
- 因为box-shadow需要依托一个div,所以我是把这个bos-shadow放在了boxShadow的div上,并设置了宽高为1px
- x,y的大小也会改变绘制出来的阴影大小,感兴趣可以尝试一下
总的来说,这个绘制不是很难,主要是挺好玩的,不过对浏览器的性能消耗很大,我在查看元素的时候经常会被卡死,轻易别尝试了。