React 实现拖动滑动条的扫描滑动效果

React 实现拖动滑动条的扫描滑动效果

  • 01 简要说明
  • 02 效果展示
    • 动态效果图:
    • 静态效果图:
  • 03 JS代码
  • 04 CSS代码
  • 05 assets中滑动条的svg代码
  • 06 组件使用

01 简要说明

  • 页面需求:两张对比图片,实现拖动滑动条展现出扫描滑动效果,增强对比的效果
  • 实现思路:一个大盒子,两张图片放在自己的小盒子里,小盒子堆叠(其中一个小盒子最初宽度为0),再来两个滑动条的图片(选中状态和未选中状态),给滑动条绑定onMouseDown事件,即当鼠标点下,再触发onMouseMove事件,监听鼠标滑动距离来修改初始宽度为0小盒子的动态宽度。
  • 注意:1. 绑定监听事件后,一定要记得销毁监听;2. 有些监听函数需要给该元素的父级绑定。
  • 代码实现虽然是基于React写的一个组件,但是能够实现多用,因为标签和获取元素基本上都是采用原生,主要是理解代码思路!

02 效果展示

动态效果图:

静态效果图:

React 实现拖动滑动条的扫描滑动效果_第1张图片
React 实现拖动滑动条的扫描滑动效果_第2张图片
React 实现拖动滑动条的扫描滑动效果_第3张图片
React 实现拖动滑动条的扫描滑动效果_第4张图片

03 JS代码

(命名:index.js

import React, { Component } from 'react';

import './style.css';
import unSelect from './assets/drag_line.svg';
import onSelect from './assets/drag_line_hover.svg';

class SwipeScan extends Component {
  constructor(props) {
    super(props);
    // 初始化state
    this.state = {
      // 滑动条初始左偏移
      initX: 0,
      // 滑动条动态左偏移 即默认显示位置
      needX: -28,
      // 滑动条的宽度
      SliderW: 0,
      // 扫描盒子宽度和高度
      boxW: 0,
      boxH: 0,
      // 滑动条选中状态
      isSelect: false,
    };
  }

  componentDidMount = () => {
    // 封装设置元素高宽的函数
    this.setWidHei();
    // 监听 当窗口大小发生变化触发
    window.addEventListener('resize', this.onResize);
  };

  // 销毁监听
  componentWillUnmount = () => {
    document.body.removeEventListener('mousemove', this.onMouseMove);
    document.body.removeEventListener('mouseup', this.onMouseUp);
    window.removeEventListener('resize', this.onResize);
  };

  // 封装设置元素高宽的函数
  setWidHei = () => {
    // 获取扫描盒子的宽度 即右边图片的大小宽度
    this.setState({
      boxW: document.getElementsByClassName('scanBox')[0].clientWidth,
      boxH: document.getElementsByClassName('scanBox')[0].clientHeight,
      SliderW: document.getElementsByClassName('slider')[0].clientWidth,
    });
  };

  // 当窗口大小发生变化触发
  onResize = () => {
    this.setWidHei();
    // eslint-disable-next-line no-restricted-globals
    location.reload();
  };

  // 当鼠标点下触发事件
  onMouseDown = (e) => {
    let { initX } = this.state;
    // 当前鼠标点下的页面位置-当前元素的左偏移=滑动条初始左偏移
    initX = e.clientX - e.target.offsetLeft;
    this.setState({ initX, isSelect: true });
    // 给body绑定鼠标移动监听事件
    document.body.addEventListener('mousemove', this.onMouseMove);
    // 给body绑定鼠标弹起监听事件
    document.body.addEventListener('mouseup', this.onMouseUp);
  };

  // 当鼠标移动触发事件
  onMouseMove = (e) => {
    const { boxW, SliderW } = this.state;
    // 动态滑动距离
    let resultX = 0;
    // 鼠标移动距离
    const MoveX = e.clientX - this.state.initX;
    if (MoveX <= -28) {
      resultX = -28;
    } else if (MoveX >= boxW - SliderW + 27) {
      resultX = boxW - SliderW + 27;
    } else {
      resultX = MoveX;
    }
    this.setState({
      needX: resultX,
    });
  };

  // 当鼠标弹起触发事件
  onMouseUp = () => {
    this.setState({ isSelect: false });
    document.body.removeEventListener('mousemove', this.onMouseMove);
    document.body.removeEventListener('mouseup', this.onMouseUp);
  };

  render() {
    const { needX, isSelect, boxW, boxH } = this.state;
    const { leftImg, rightImg } = this.props;
    // 滑动条的左边偏移量
    const sliderStyle = {
      left: needX,
      // 如果滑动条是选中状态则选择第一个背景图片,否则第二个
      backgroundImage: isSelect ? `url(${onSelect})` : `url(${unSelect})`,
    };
    // 修改左边盒子的宽度和高度,避免页面大小变化时出现不对齐的情况
    const rightBoxStyle = {
      width: needX + 28,
      height: boxH,
    };
    // 固定左边图片的高宽,避免页面大小变化时出现不对齐的情况
    const rightImgStyle = {
      width: boxW,
      height: boxH,
    };
    return (
      <div className='scanBox noSelect' onMouseUp={this.onMouseUp}>
        {/* 左边盒子 */}
        <div className='left noSelect' style={rightBoxStyle}>
          {/* 左边图片 */}
          <img
            alt='after'
            src={leftImg}
            className='noDrag'
            style={rightImgStyle}
          />
        </div>
        {/* 滑动条 */}
        <div
          onMouseDown={this.onMouseDown}
          className='slider'
          style={sliderStyle}
        ></div>
        {/* 右边盒子 */}
        <div className='right noSelect'>
          {/* 右边盒子 */}
          <img alt='before' src={rightImg} className='rightPic noDrag' />
        </div>
      </div>
    );
  }
}

export default SwipeScan;

04 CSS代码

(命名:style.css

/* 图片不可拖动 */
.noDrag {
  -webkit-user-drag: none;
}
/* div不可选中 */
.noSelect {
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
/* 扫描盒子 */
.scanBox {
  position: relative;
  margin: 100px auto;
  /* 高度宽度可根据实际情况配置 */
  width: 800px;
  height: 500px;
  overflow: hidden;
}
/* 解译按钮 */
.scanBox .scanBtn {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 5;
  padding: 0px 20px;
  font-size: 18px;
  transform: translate(-50%, -50%);
}
/* 滑动条 */
.scanBox .slider {
  position: absolute;
  top: 0px;
  z-index: 3;
  width: 56px;
  height: 100%;
  background-image: url(assets/drag_line.svg);
  background-repeat: no-repeat;
  background-size: 100% 100%;
}
/* 左盒子 */
.scanBox .left {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
/* 左图片 */
.scanBox .left .leftPic {
  width: 100%;
  height: 100%;
}
/* 右盒子 */
.scanBox .right {
  position: absolute;
  top: 0;
  left: 0px;
  z-index: 1;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
/* 右图片 */
.scanBox .right .rightPic {
  width: 100%;
  height: 100%;
}

05 assets中滑动条的svg代码

可以按需求更改svg代码,或者直接用上喜欢的图片

未点中状态(命名:drag_line.svg):

<svg xmlns="http://www.w3.org/2000/svg" width="58" height="1019.5" viewBox="0 0 58 1019.5">
    <g id="组_30" data-name="组 30" transform="translate(-999 -127.5)">
        <line id="直线_21" data-name="直线 21" y2="480.5" transform="translate(1028.5 666.5)" fill="none" stroke="#adadad" stroke-width="2"/>
        <line id="直线_22" data-name="直线 22" y2="481" transform="translate(1028.5 127.5)" fill="none" stroke="#adadad" stroke-width="2"/>
        <g id="椭圆_12" data-name="椭圆 12" transform="translate(999 608)" fill="none" stroke="#adadad" stroke-width="2">
            <circle cx="29" cy="29" r="29" stroke="none"/>
            <circle cx="29" cy="29" r="28" fill="none"/>
        g>
        <g id="组_29" data-name="组 29" transform="translate(1010 627.295)">
            <path id="多边形_1" data-name="多边形 1" d="M10.5,0,21,11H0Z" transform="translate(0 20.705) rotate(-90)" fill="#adadad"/>
            <path id="多边形_2" data-name="多边形 2" d="M10.5,0,21,11H0Z" transform="translate(36 -0.295) rotate(90)" fill="#adadad"/>
        g>
    g>
svg>

点中状态(命名:drag_line_hover.svg):

<svg xmlns="http://www.w3.org/2000/svg" width="58" height="1019.5" viewBox="0 0 58 1019.5">
    <g id="组_30" data-name="组 30" transform="translate(-999 -127.5)">
        <line id="直线_21" data-name="直线 21" y2="480.5" transform="translate(1028.5 666.5)" fill="none" stroke="#1890ff" stroke-width="2"/>
        <line id="直线_22" data-name="直线 22" y2="481" transform="translate(1028.5 127.5)" fill="none" stroke="#1890ff" stroke-width="2"/>
        <g id="椭圆_12" data-name="椭圆 12" transform="translate(999 608)" fill="none" stroke="#1890ff" stroke-width="2">
            <circle cx="29" cy="29" r="29" stroke="none"/>
            <circle cx="29" cy="29" r="28" fill="none"/>
        g>
        <g id="组_29" data-name="组 29" transform="translate(1010 627.295)">
            <path id="多边形_1" data-name="多边形 1" d="M10.5,0,21,11H0Z" transform="translate(0 20.705) rotate(-90)" fill="#1890ff"/>
            <path id="多边形_2" data-name="多边形 2" d="M10.5,0,21,11H0Z" transform="translate(36 -0.295) rotate(90)" fill="#1890ff"/>
        g>
    g>
svg>

06 组件使用

这个具体要看你怎么使用了,我是直接导入在项目里的index.js。(组件命名:SwipeScan

// 导入组件
import SwipeScan from './SwipeScan';
// 导入图片
import leftImg from './assets/leftImg.png';
import rightImg from './assets/rightImg.png';

// 渲染
ReactDOM.render(
  <div>
    // 使用组件,传递图片
    <SwipeScan leftImg={leftImg} rightImg={rightImg} />
  </div>,
  document.getElementById('root')
);

你可能感兴趣的:(React,js,css,reactjs,组件化)