• 创意的气泡滑块拖动vue特效


    基于vue制作手机端气泡滑块拖动ui交互,线条上滑块拖动气泡提示ui特效。

    Html代码:

    <!DOCTYPE html>
    <html lang="en" >
    <head>
      <meta charset="UTF-8">
      <title>创意的气泡滑块拖动vue特效</title>
    <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
    <main>
      <div class="sliderContainer">
        <div class="ballon" ref="ballonEl">
          <span class="ballon__text" ref="ballonTextEl">{{ percent }}</span>
        </div>
        <div v-on:mousedown="dragStart" v-on:touchstart="dragStart" ref="thumbEl" class="sliderContainer__thumb"></div>
        <div class="sliderContainer__input" ref="inputEl"></div>
      </div>
    </main>
    <script src='js/vue.min.js'></script>
    <script src='js/hammer.min.js'></script>
    <script src='js/TweenMax.min.js'></script>
    <script src="js/script.js"></script>
    </body>
    </html>

    css代码:

    :root {
      --color-primary: #5626e8;
      --color-secondary: #d2daef;
      --color-bg: #f4f6fe;
    }
    
    * {
      box-sizing: border-box;padding:0;margin:0;
    }
    
    body {
      background-color: var(--color-bg);
    }
    
    main {
       100%;
      min-height: 100vh;
      display: -webkit-box;
      display: flex;
      -webkit-box-pack: center;
              justify-content: center;
      -webkit-box-align: center;
              align-items: center;
    }
    
    .sliderContainer {
      position: relative;
       100%;
      display: -webkit-box;
      display: flex;
      -webkit-box-orient: vertical;
      -webkit-box-direction: normal;
              flex-direction: column;
      -webkit-box-align: center;
              align-items: center;
      padding: 3em;
    }
    
    .sliderContainer .sliderContainer__input {
       100%;
      -webkit-appearance: none;
      -moz-appearance: none;
           appearance: none;
      outline: none;
      height: 0.2em;
      background-image: -webkit-gradient(
        linear,
        left top,
        right top,
        color-stop(50%, var(--color-primary)),
        color-stop(50%, var(--color-secondary))
      );
      border-radius: 1em;
      margin: 0;
    }
    
    .sliderContainer .sliderContainer__thumb {
      cursor: pointer;
       1.5em;
      height: 1.5em;
      border: 0.5em solid var(--color-primary);
      border-radius: 100%;
      background-color: white;
      position: absolute;
      top: 50%;
      -webkit-transform: translateY(-50%);
              transform: translateY(-50%);
      -webkit-user-select: none;
         -moz-user-select: none;
          -ms-user-select: none;
              user-select: none;
    }
    
    .ballon {
      --size: 4em;
    
       var(--size);
      height: var(--size);
      display: -webkit-box;
      display: flex;
      -webkit-box-pack: center;
              justify-content: center;
      -webkit-box-align: center;
              align-items: center;
      background-color: var(--color-primary);
      border-bottom-left-radius: 50%;
      position: absolute;
      border-top-left-radius: 50%;
      border-top-right-radius: 50%;
      top: -1.2em;
      opacity: 0;
    }
    
    .ballon .ballon__text {
      color: white;
      -webkit-transform: rotate(-45deg);
              transform: rotate(-45deg);
      font-family: Arial, Helvetica, sans-serif;
    }
    
    .ballon::after {
      content: "";
      position: absolute;
       0;
      height: 0;
      border-left: 0.25em solid transparent;
      border-right: 0.25em solid transparent;
      -webkit-transform: rotate(-45deg);
              transform: rotate(-45deg);
      border-bottom: 0.25em solid var(--color-primary);
      top: 98%;
      left: 94%;
    }
    
    @media (max- 768px) {
      .sliderContainer {
        min- 95%;
      }
    }
    let currentDeltaX = 0;
    
    //Utils
    const getElementOffset = el => el.getBoundingClientRect();
    
    const getPercentInBetween = ({ value, min, max }) => {
      return (value - min) / (max - min) * 100;
    };
    
    const getValueInBetween = (value, { min, max }) => {
      if (value < min) return min;
      if (value > max) return max;
      return value;
    };
    
    const getSliderValue = () => {
      const getRef = ref => app.$refs[ref];
      const { left: thumbElLeft,  thumbElWidth } = getElementOffset(
      getRef("thumbEl"));
    
      const { left: inputElLeft, right: inputElRight } = getElementOffset(
      getRef("inputEl"));
    
      const thumbCenterPoint = thumbElLeft + thumbElWidth / 2;
    
      const rangeValue = getPercentInBetween({
        value: thumbCenterPoint,
        min: inputElLeft,
        max: inputElRight });
    
    
      const percent = Math.round(
      getValueInBetween(rangeValue, { min: 0, max: 100 }));
    
    
      return percent;
    };
    
    const getBallonScaleValue = () =>
    1 + getValueInBetween(getSliderValue(), { min: 1, max: 99 }) / 480;
    
    //-------------------
    
    const app = new Vue({
      el: "main",
      data: {
        isDragging: false,
        percent: 50,
        initialThumbElClientRect: null },
    
      mounted: function () {
        const getRef = ref => this.$refs[ref];
    
        TweenLite.set(getRef("thumbEl"), {
          y: "-50%" });
    
    
        TweenLite.set(getRef("ballonEl"), {
          rotation: 45 });
    
    
        this.initialThumbElClientRect = getElementOffset(getRef("thumbEl"));
      },
      methods: {
        getRef: function (ref) {
          return this.$refs[ref];
        },
        dragStart: function () {
          this.isDragging = true;
          TweenLite.set(this.getRef("ballonEl"), {
            rotation: 45 });
    
          this.percent = getSliderValue();
          this.toggleSliderAnimation();
        },
        dragEnd: function () {
          this.isDragging = false;
          this.toggleSliderAnimation();
        },
        drag: function (event) {
          if (this.isDragging) {
            const { deltaX: newDeltaX, velocityX } = event;
            const {
              left: thumbElInitialOffsetLeft,
              right: thumbElInitialOffsetRight,
               thumbElWidth } =
            this.initialThumbElClientRect;
            const {
              left: inputElOffsetLeft,
              right: inputElOffsetRight } =
            getElementOffset(this.getRef("inputEl"));
    
            const xMovement = newDeltaX + currentDeltaX;
    
            const hasGoneTooLeft =
            thumbElInitialOffsetLeft + xMovement <
            inputElOffsetLeft - thumbElWidth / 2;
            const hasGoneTooRight =
            thumbElInitialOffsetRight + xMovement >
            inputElOffsetRight + thumbElWidth / 2;
    
            if (hasGoneTooLeft || hasGoneTooRight) return;
    
            this.moveThumb({ x: xMovement, velocityX });
          }
        },
        moveThumb: function ({ x, velocityX }) {
          const sliderValue = getSliderValue();
          const tl = new TimelineLite();
    
          const scaleValue = getBallonScaleValue();
    
          tl.
          to(this.getRef("thumbEl"), 0.001, {
            x,
            ease: Power4.ease }).
    
          to(this.getRef("ballonEl"), 0.2, {
            x,
            scale: scaleValue,
            rotation:
            45 + getValueInBetween(velocityX, { min: -7, max: 7 }) * -15,
            ease: Power4.ease });
    
    
          this.getRef("inputEl").style.backgroundImage = `-webkit-gradient(
            linear,
            left top,
            right top,
            color-stop(${sliderValue}%, var(--color-primary)),
            color-stop(${sliderValue}%, var(--color-secondary))
          )`;
    
          this.percent = sliderValue;
        },
    
        toggleSliderAnimation: function () {
          const that = this;
          const tl = new TimelineLite();
    
          const sliderValue = getSliderValue();
          const scaleValue = this.isDragging ? getBallonScaleValue() : 0.8;
          const shouldFlyBalloon = sliderValue >= 80 && sliderValue <= 100;
    
          const toggleThumbAnim = () => {
            tl.to(that.getRef("thumbEl"), 0.23, {
               that.isDragging ? "2.6em" : "1.5em",
              height: that.isDragging ? "2.6em" : "1.5em",
              borderWidth: that.isDragging ? "2px" : "0.5em",
              ease: Power4.easeOut });
    
          };
    
          const toggleBallonAnim = () => {
            tl.to(
            that.getRef("ballonEl"),
            0.5,
            {
              opacity: that.isDragging ? 1 : 0,
              scale: scaleValue,
              y: that.isDragging ? "-75%" : "5%",
              ease: that.isDragging ? Power1.easeOut : Power1.easeIn },
    
            "-0.23");
    
          };
    
          if (this.isDragging) {
            toggleThumbAnim();
            toggleBallonAnim();
          } else {
            toggleThumbAnim();
            if (shouldFlyBalloon) {
              tl.to(this.getRef("ballonEl"), 1.7, {
                y: -window.innerHeight / 2,
                opacity: 0,
                ease: Power1.easeOut,
                onComplete: () => {
                  TweenLite.set(this.getRef("ballonEl"), {
                    y: "5%" });
    
                } });
    
            } else {
              toggleBallonAnim();
            }
          }
        } } });
    
    
    
    const getRef = ref => app.$refs[ref];
    
    const appHammer = new Hammer.Manager(getRef("thumbEl"));
    appHammer.add(new Hammer.Pan({ threshold: 0 }));
    appHammer.on("pan", app.drag);
    appHammer.on("panend", ({ deltaX }) => {
      currentDeltaX += deltaX;
    });
    
    window.addEventListener("mouseup", app.dragEnd, false);
    window.addEventListener("touchend", app.dragEnd, false);
  • 相关阅读:
    ASP.NET2.0服务器控件之自定义状态管理
    sql中RIGHT和LEFT(小技巧)
    微软云计算的思考
    枚举转化和判断方法的总结
    sql中的split(小技巧)
    ASP.net实现无扩展名的URL重写。简单、方便、无需ISAPI
    浅析值类型与引用类型的内存分配
    ASP.Net Web Page深入探讨
    const和static readonly 区别
    程序中一种用in的sql防注入的方法(小技巧)
  • 原文地址:https://www.cnblogs.com/ZXH-null/p/13139697.html
Copyright © 2020-2023  润新知