实现一个图片滑块组件

158 阅读2分钟

S: 实现一个组件,会跟随鼠标移动,改变左右图片的大小,但是图片并不放缩

思路: 一开始尝试使用background,通过改变两边的宽度来实现,但是实现发现,右边的图片其实是会放缩的,这时候通过查阅资料发现,可以通过clip-path来实现。

clip-path属性是CSS3中的一个属性,用于指定元素的剪辑区域。它可以用来裁剪元素的可见部分,使其呈现出不规则的形状或剪影效果。

clip-path属性的值可以是各种形状,包括基本形状(如矩形、圆形、椭圆等)以及路径形状(通过SVG路径数据描述)。以下是一些常用的clip-path属性值:

  1. 矩形形状:

    • clip-path: inset(10px 20px 30px 40px);:指定一个矩形的内边距,裁剪元素的可见区域为指定矩形范围内。
  2. 圆形形状:

    • clip-path: circle(50%);:指定一个以元素中心为圆心的圆形,裁剪元素的可见区域为圆形内部。
  3. 椭圆形状:

    • clip-path: ellipse(50% 50% at 50% 50%);:指定一个以元素中心为焦点的椭圆形,裁剪元素的可见区域为椭圆形内部。
  4. 自定义路径形状(通过SVG路径数据):

    • clip-path: path('M50 0 L100 50 L50 100 L0 50 Z');:通过SVG路径数据描述自定义的路径形状,裁剪元素的可见区域为路径内部。

使用clip-path属性时,可以将其应用于任何具有可见效果的元素,如divimg等。请注意,clip-path属性的兼容性可能会因浏览器而异,特别是一些旧版浏览器对其支持较差。为了获得更好的兼容性,可以考虑使用兼容性较好的CSS前缀或使用其他方法实现类似的效果,如使用mask属性或使用SVG元素作为剪辑路径。

具体实现:

<template>
    <div 
    class="container"
    ref="container"
    :style="{height: `${height}px`}"
    @mouseenter="handleMouseEnter"
    @mouseleave="handleMouseLeave"
    >
        <div 
        class="left-img img"
        ref="left"
        :style="{backgroundImage: `url(${originImg})`}"
        ></div>
        <div class="resizer" ref="resizer">
            <div class="axisx"></div>
            <div class="btn"><div class="blur" /></div>
            <div class="axisx"></div>
        </div>
        <div class="right-img img"
            ref="right"
            :style="{backgroundImage: `url(${patternImg})`}"
        ></div>
    </div>
</template>

<script>
export default {
    name: 'pictureSlider',
    props: {
        originImg: {
            type: String,
            default: ''
        },
        patternImg: {
            type: String,
            default: ''
        },
        height: {
            type: Number,
            default: 400
        }
    },
    data(){
        return {
            x: 0,
            leftWidth: 0
        }
    },
    beforeDestory(){
        this.handleMouseLeave()
    },
    method: {
        handleMouseDown(e){
            e.preventDefault();
            this.x = e.clientX;
            this.leftWidth = this.$refs.left.getBoundingClientRect().width;
        },
        handleMouseEnter(){
            document.addEventListen('mousemove', this.handleMouseMove);
        },
        handleMouseMove(e){
            if(!this.$refs.container) return
            const rect = this.$refs.container.getBoundingClientRect();
            const dx = e.clientX - rect.left;
            let newLeftWidth = (dx * 100)/ rect.width;
            newLeftWidth = Math.max(newLeftWidth, 0);
            newLeftWidth = Math.min(newLeftWidth, 100);
            this.$refs.left.style.clipPath = `insert(0 ${rect.width -dx}px 0 0)`;
            this.$refs.right.style.clipPath = `insert(0 0 0 ${dx})`;
            this.$refs.resizer.style.left = `${newLeftWidth}%`;
        },
        handleMouseLeave(){
            document.removeEventListener('mousemove', this.handleMouseMove);
        },
        reset(){
            this.$refs.left.style.clipPath = `inset(0 50% 0 0 )`;
            this.$refs.right.style.clipPath = `inset(0 0 0 50%)`;
            this.$refs.resizer.style.left = `${newLeftWidth}%`;
        }
    }
}
</script>
<style lang="less" scoped>
.container {
    position: relative;
    overflow: hidden;
    .img {
        position: absolute;
        top: 0;
        height: 100%;
        width: 100%;
        background-size: cover;
        background-repeat: no-repeat;
        background-position: center;
        left: 0;
    }
    .left-img {
        z-index: 1;
        clip-path: inset(0 50% 0 0);
    }
    .right-img {
        clip-path: inset(0 0 0 50%);
        z-index: 2;
    }
    .resizer {
        position: absolute;
        left: 50%;
        top: 0;
        height: 100%;
        cursor: ew-resize;
        z-index: 999;
        display: flex;
        flex-direction: column;
        align-items: center;
        transform: translate(-50%);
        .axisx {
            flex: 1;
            width: 2px;
            background-color: #fff;
        }
        .btn {
            height: 32px;
            width: 32px;
            border-radius: 50%;
            border: 2px solid #fff;
            cursor: ew-resize;
            z-index: 9999;
            background: url('~@/assets/image/pictureSlider.svg') no-repeat;
            background-color: rgba(255, 255, 255, 0.1);
            background-position: center;
            .blur {
                width: 100%;
                heigth: 100%;
                border-radius: 50%;
                backdrop-filter: blur(0.5px); // 毛玻璃
            }
        }
    }
}
</style>