likes
comments
collection
share

JS简单操作像素设置背景图片拉伸区域避免图片拉伸变形

作者站长头像
站长
· 阅读数 14

废话开篇:简单设置图片拉伸区域,实现图片当作标签背景图时不被拉伸变形

一、实现效果

左右两个标签,左面的设置了一个不重复的背景图片

JS简单操作像素设置背景图片拉伸区域避免图片拉伸变形

现在进行一下操作,让左面的背景图在不变形的前提下进行铺满整个左侧的标签

JS简单操作像素设置背景图片拉伸区域避免图片拉伸变形

可以看到左侧的标签在背景图片不变形的情况下铺满了。但是,这里有个明显的问题,就是上面的图中间“凹进去”的部分不在布局正中了,是的,其实,在设置的时候,就是将图片上半部分的一个“重复”像素进行了重复覆盖。

更换一个图片试试。

JS简单操作像素设置背景图片拉伸区域避免图片拉伸变形

这张图中间的部分可以作为重复的“填充像素

JS简单操作像素设置背景图片拉伸区域避免图片拉伸变形

二、代码实现

1、HTML

调用很简单,初始化 PictureExtension 操作对象进行相关绑定及操作

// 进行背景延展操作
let pictureExtension = new PictureExtension(document.getElementById('backImageOne'),'./back1.png',(size)=>{
    // 需要进行拉伸的区域,因为是纵向拉伸,这里的拉伸区域的宽度为图片等宽
    let x = 0
    let y = 470
    let width = size.width
    let height = 20
    return {x,y,width,height}
})
2、JS

PictureExtension 对象管理类,实现相关拉伸过程中的像素填充操作

class PictureExtension{

    // 需要设置的背景图片的标签
    el
    // 背景图资源地址
    imgSrc
    // 图片尺寸
    imageSize = {}
    // 设置拉伸区域回调
    canStretchSizeCallBack = null
    // 设置拉伸区域
    canStretchSize = {}
    // 原像素
    originalPiexls

    constructor(el,imgSrc,callBack){
        this.el = el
        this.imgSrc = imgSrc
        this.canStretchSizeCallBack = callBack
        this.beginImageOption()
    }

    // 开始对齐图片
    async beginImageOption(){
        await this.getImageSize(this.imgSrc)
        try {
            let piexls = this.createNewBackImage()
            let url = this.createURLByPiexls(piexls)
            this.el.style.backgroundImage = 'url('+url +')';
        } catch (error) {
            console.log('error:' + error)
        }
    }

    // 获取图片大小
    async getImageSize(imgSrc){
        var image = new Image();
        image.src = imgSrc;
        await new Promise((resolve)=>{
            image.onload = resolve
        })
        let width = image.width
        let height = image.height
        this.imageSize = { width,height }
        // 保存当前的自定义设置的拉伸区域
        if(this.canStretchSizeCallBack){
            this.canStretchSize = this.canStretchSizeCallBack(this.imageSize)
        }
        let canvas = document.createElement('canvas')
        canvas.setAttribute('width',`${width}px`)
        canvas.setAttribute('height',`${height}px`)
        var ctx = canvas.getContext("2d")
        ctx.fillStyle = ctx.createPattern(image, 'no-repeat');
        ctx.fillRect(0, 0, width, height);
        try {
            //保存像素
            this.originalPiexls = ctx.getImageData(0,0,width,height)
        } catch (error) {
            console.log(error)
        }
    }

    // 获取尺寸
    getAllNeedSize(){
        // 当前需要操作的标签实际尺寸
        let elWidth = this.el.offsetWidth
        let elHeight = this.el.offsetHeight

        // 当前图片的实际尺寸
        let imgWidth = this.imageSize.width
        let imgHeight = this.imageSize.height

        // 创建新图片的尺寸,根据dom对象尺寸进行比例缩放
        let finallyWidth = imgWidth
        let finallyHeight = imgWidth * (elHeight / elWidth)

        // 需要补的像素
        let needFillPiexlHeight = finallyHeight - imgHeight
        return { elWidth,elHeight,imgWidth,imgHeight,finallyWidth,finallyHeight,needFillPiexlHeight }
    }

    // 生成新的图片
    createNewBackImage(){
        if(this.originalPiexls.data.length == 0){
            throw('像素为空')
        }
        let { finallyWidth,finallyHeight,needFillPiexlHeight } = this.getAllNeedSize()
        // 创建新的图片
        let currentIndex = 0
        let finallyPiexls = new Uint8ClampedArray(finallyWidth * finallyHeight * 4)
        for(let y = 0;y < finallyHeight;y ++){
            for(let x = 0;x < finallyWidth;x ++){
                for(let k = 0;k < 4;k++){
                    let index
                    if(y <= this.canStretchSize.y) {
                        // 开头直接对接
                        index = (y * finallyWidth + x) * 4 + k
                    } else if( y > this.canStretchSize.y && y <= this.canStretchSize.y + needFillPiexlHeight ){
                        // 中间重复补像素
                        index = (((y % this.canStretchSize.height) + this.canStretchSize.y) * finallyWidth + x) * 4 + k
                    } else {
                        // 后面错位对齐
                        index = ((y - needFillPiexlHeight) * finallyWidth + x) * 4 + k
                    }
                    finallyPiexls[currentIndex] = this.originalPiexls.data[index]
                    currentIndex ++
                }
            }
        }
        return finallyPiexls
    }

    // 通过像素点生成URL
    createURLByPiexls(piexls){
        let url = ''
        let { finallyWidth,finallyHeight } = this.getAllNeedSize()
        let canvas = document.createElement('canvas')
        canvas.setAttribute('width',`${finallyWidth}px`)
        canvas.setAttribute('height',`${finallyHeight}px`)
        let smallCtx = canvas.getContext("2d")
        //初始化ImageData
        let finallyImageData = new ImageData(piexls,finallyWidth,finallyHeight)
        // 当前范围内需要放大的像素
        smallCtx.putImageData(finallyImageData,0,0,0,0,finallyWidth,finallyHeight)
        url = canvas.toDataURL('image/jpeg',1)
        if(url == ''){
            throw('像素转图片失败')
        }
        return url
    }
}
3、逻辑

上面就是全部的逻辑,最终想干的事情就是当填充标签尺寸与背景图片尺寸不一致的时候,将背景图片进行强制拉伸,过程中设置一下“可复制”的拉伸区域,为了保证不变形,无限复制里面的像素值直至填满所有空缺的点。

三、思考与总结

有些复杂的样式CSS可操作性太小,不如直接用JS操作来实现它,代码拙劣,大神勿笑[抱拳][抱拳][抱拳],哪怕能帮助大家一点点,深感欣慰!

转载自:https://juejin.cn/post/7202501172329365565
评论
请登录