实现动态拉伸背景图

2,564 阅读1分钟

背景

众所周知,Android中存在点九图,即将一张图片分为9块,四角固定,其他区域进行横向、纵向拉伸从而满足自适应大小的背景功能。

但是在前端Web工程中如何实现这种功能呢? 这篇文章简化该问题:实现上下两部分固定百分比,中间自由拉伸

已有技术

1.使用border-image实现点九图 border-image主要考虑兼容性问题

使用技术思考

  1. 如何让背景图动态根据内容伸缩(纯CSS

    内容使用文档流书写,背景div使用绝对定位获取到父元素大小

    <!-- lang = vue -->
    <div class="vendor-outbox"><!-- position: relative -->
        <div class="flc fl-a-c" style="position: relative;">
            <div class="vendor-list-box fl fl-w fl-j-c">
                <template v-for="(item, index) in vendors">
                    <img
                        v-if="item"
                        class="vendor-item"
                        @click="openLink(item)"
                        :src="item.imgSrc"
                    />
                    <div v-else class="vendor-item"></div>
                </template>
            </div>
        </div>
        <CustomBackgroundImage></CustomBackgroundImage>
    </div>
    

    该代码为商户列表,使用flex布局一行两个,进行自动延伸

  2. 如何让图片仅展示一部分(纯CSS

    使用overflow:hidden + transfrom: translateY()进行隐藏

    <!-- lang = vue -->
    <div class="custombackground-t">
        <img :src="imgSrc" style="width: 100%;transform:translateY(50%)" />
    </div>
    
    .custombackground-t {
        overflow: hidden;
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        transform: translateY(-50%);
    }
    
  3. 如何让div获取到剩余部分大小

    在当前这个问题中,父元素的高度是不确定的,上部分的高度为图片高度的固定百分比,下部分的高度为图片的固定百分比,中间使用剩下的高度(暂未想到纯CSS的方式,目前使用JS实现)

    先获取到图片实际宽高,然后动态进行计算

实际使用代码

<template>
    <div class="ab-full" style="z-index: -1" ref="imgRef"> <!-- 该部分注意必须覆盖在实际内容之下 -->
        <!-- 下面是中间模块 -->
        <div
            class="custombackground-c"
            :style="{ top: `${imgRealHeight * topPercent / 100}px`, bottom: `${imgRealHeight * bottomPercent / 100}px` }"
        >
            <img
                :src="imgSrc"
                style="width: 100%;"
                :style="{ transform: `translateY(-50%) scaleY(${centerScale})` }"
            />
        </div>
        <!-- 下面是top模块 -->
        <div class="custombackground-t" :style="{ transform: `translateY(${topPercent - 100}%)` }">
            <img
                :src="imgSrc"
                style="width: 100%;"
                :style="{ transform: `translateY(${100 - topPercent}%)` }"
            />
        </div>
        <!-- 下面是bottom模块 -->
        <div
            class="custombackground-b"
            :style="{ transform: `translateY(${100 - bottomPercent}%)` }"
        >
            <img
                :src="imgSrc"
                style="width: 100%;"
                :style="{ transform: `translateY(${bottomPercent - 100}%)` }"
            />
        </div>
    </div>
</template>
<script>
export default {
    name: 'CustomBackgroundImage',
    props: {
        imgSrc: { // 实际背景图url
            type: String,
            required: true
        },
        topPercent: { // 头部占图片的百分比
            type: Number,
            default: 50
        },
        bottomPercent: { // 底部占图片的百分比
            type: Number,
            default: 10
        }
    },
    data() {
        return {
            width: 0, // 图片的实际宽度
            height: 0, // 图片的实际高度
        }
    },
    watch: {
        imgSrc: { // 当图片url改变时,实时重新拉取图片的实际宽高
            immediate: true,
            handler(val) {
                if (val) {
                    const i = new Image()
                    i.onload = (e) => {
                        this.width = i.width
                        this.height = i.height
                        i.onload = null
                    }
                    i.src = val
                } else {
                    this.width = 0
                    this.height = 0
                }
            }
        },
    },
    computed: {
        imgRealHeight() { // 根据实际占用宽度,计算出实际占用高度(保持图片的长宽比不变)
            const val = this.width && this.$refs.imgRef ? this.height * this.$refs.imgRef.clientWidth / this.width : 0
            return val
        },
        centerScale() { // 计算出中间部分的缩放比率
            if (!this.$refs.imgRef) {
                this.centerScale = 1
            } else {
                const allPercent = (this.topPercent + this.bottomPercent) / 100
                this.centerScale = (this.$refs.imgRef.clientHeight - this.imgRealHeight * allPercent) / (this.imgRealHeight * (1 - allPercent))
            }
        }
    },
}
</script>
<style>
.custombackground-t {
    overflow: hidden;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
}
.custombackground-b {
    overflow: hidden;
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
}
.custombackground-c {
    overflow: hidden;
    position: absolute;
    left: 0;
    right: 0;
}
</style>

初步想法,欢迎讨论,实现高兼容性下的背景缩放功能