依赖插件如下:
https://www.npmjs.com/package/panzoom(9.4.3)
https://www.npmjs.com/package/decimal.js/v/10.2.0(10.2.0)
https://nightcatsama.github.io/vue-slider-component/#/api/slots(vue-slider-component)

效果图:

代码如下(vue 版本 2.6.11):

<template>
  <div class="page-wrapper">
    <div class="lft">
      <div>Left</div>
    </div>
    <div class="rht">
      <div
        class="rht-top"
        style="background: rgb(239, 226, 250); height: 120px"
      >
        xxxxx
      </div>
      <div class="rht-content">

        <div style="position: absolute; top: 10px; left: 10px; z-index: 1; background: #fff; padding: 10px;">缩放:按住alt键盘 + 鼠标滚动</div>

        <div
          style="
            background: #fff;
            padding: 5px;
            position: absolute;
            z-index: 1;
            bottom: 10px;
            left: 10px;
            display: flex;
            align-items: center;
            z-index: 1;
          "
        >
          <i class="el-icon-remove-outline" @click="zoomIn(-1)"></i>

          <!-- 参考: https://nightcatsama.github.io/vue-slider-component/#/api/slots -->
          <vue-slider
            v-model="canvasInfo.scale"
            :min="sliderMin"
            :max="sliderMax"
            :interval="interval"
            @change="handleChagneScale"
            :tooltip-formatter="tooltipFormatter"
            style="width: 100px; margin: 0 10px"
          >
          </vue-slider>
          <i class="el-icon-circle-plus-outline" @click="zoomIn(1)"></i>
        </div>

        <div class="nodes-content-wrapper" id="nodes-content-wrapper">
          <div class="nodes-wrapper" id="nodes-wrapper">
            <div style="font-size: 80px; height: 100%; display: flex; align-items: center; justify-content: center;">测试字符</div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import panzoom from "panzoom";
import { Decimal } from "decimal.js";

export default {
  data() {
    return {
      pan: null,
      sliderMin: 0.2,
      sliderMax: 3,
      interval: 0.01,
      canvasInfo: {
        scale: 0.8,
      },
      tooltipFormatter: (v) => `${this.getPercentage(v)}`,
    };
  },

  mounted() {
    this.initPanZoom(this.canvasInfo.scale, 100, 50);
  },
  methods: {
    initPanZoom(scale = 1, x = 0, y = 0) {
      let that = this;

      if (that.pan) {
        that.pan.dispose();
      }

      const mainContainer = document.getElementById("nodes-wrapper");

      let params = {
        smoothScroll: false,
        bounds: false, //可以移动出父容器
        // autocenter: true,
        zoomDoubleClickSpeed: 1,
        minZoom: this.minZoom,
        maxZoom: this.maxZoom,
        // overflow: 'auto',
        // contain: 'outside',
        beforeWheel: (e) => {
          let shouldIgnore = !e.altKey;
          if (!shouldIgnore) {
            //按下了 alt 键
            if (e.deltaY > 0) {
              this.zoomIn(-1, 0.1);
            } else if (e.deltaY < 0) {
              this.zoomIn(1, 0.1);
            }
          }

          return true; //不需要自带的滚动放大缩小

          // // 仅当 altKey 按下时才允许滚轮缩放。否则 - 忽略
          // //暂时不支持滚轮放大缩小,还没有和拖拽同步
          // let shouldIgnore = !e.altKey;
          // return shouldIgnore;
        },
        beforeMouseDown: function (e) {},
      };

      if (scale) {
        params.initialZoom = scale;
        // that.jsp.setZoom(scale);
      }

      that.pan = panzoom(mainContainer, params);
      that.pan.moveTo(x, y);
    },
    getContentClientRect() {
      let container = document.getElementById("nodes-content-wrapper");
      let rect = container.getBoundingClientRect();
      return rect;
    },

    getCenter() {
      let rect = this.getContentClientRect();
      let cx = rect.x + rect.width / 2;
      let cy = rect.y + rect.height / 2;
      return {
        cx,
        cy,
      };
    },

    zoomIn(type = 1, step = 0.01) {
      let center = this.getCenter();
      let scale =
        type > 0
          ? (this.canvasInfo.scale += step)
          : (this.canvasInfo.scale -= step);

      if (type == 1) {
        //放大
        scale = scale >= this.sliderMax ? this.sliderMax : scale;
      } else if (type == -1) {
        //缩小
        scale = scale <= this.sliderMin ? this.sliderMin : scale;
      }

      this.canvasInfo.scale = scale;
      this.pan.zoomAbs(center.cx, center.cy, this.canvasInfo.scale);
    },
    getPercentage(val) {
      return new Decimal(val).mul(new Decimal(100)).toFixed(0) + "%";
    },
    handleChagneScale(val) {
      let center = this.getCenter();
      this.pan.zoomAbs(center.cx, center.cy, this.canvasInfo.scale);
      this.canvasInfo.scale = val;
    },
  },
};
</script>

<style scoped lang="scss">
.page-wrapper {
  height: 100%;
  display: flex;
  .lft {
    width: 300px;
    flex-shrink: 0;
  }
  .rht {
    flex: 1;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    .rht-top {
      flex-shrink: 0;
    }
    .rht-content {
      flex: 1;
      position: relative;
      width: 100%;
      height: 100%;
      overflow: hidden;
      .nodes-content-wrapper {
        background: #f1f1f1;
        position: relative;
        width: 100%;
        height: 100%;
        .nodes-wrapper {
          background: yellow;
          position: relative;
          width: 100%;
          height: 100%;
        }
      }
    }
  }
}
</style>


qngyun1029
969 声望15 粉丝

引用和评论

0 条评论