vue2项目使用zoom解决pc端浏览器百分比缩放,布局样式不兼容问题

一、在utils文件夹下面建计算zoom的js文件(resizeMixins.js),如果使用elemen-ui框架,解决框架里面定位的相关问题(element-zoom-bug.js)

a.resizeMixins.js的js

javascript 复制代码
// 混入代码
import "@/utils/element-zoom-bug.js";
export default {
  data() {
    return {
      resizeData: {
        width: 1920, //设计稿宽
        height: 1080, //设计稿高
        ratio: 16 / 9, //比例
      },
    };
  },
  created() {
    this.setBodyZoom();
    // window.addEventListener("resize", this.setBodyZoom, false);
    // window.addEventListener(
    //   "pageshow",
    //   function (e) {
    //     if (e.persisted) {
    //       // 浏览器后退的时候重新计算
    //       this.setBodyZoom();
    //     }
    //   },
    //   false
    // );
  },
  beforeDestroy() {
    // window.removeEventListener("reisze", this.setBodyZoom());
  },
  methods: {
    initAutofitByWidth(designWidth, designHeight, selector) {
      const el = document.querySelector(selector || "body");
      el.style.zoom = 1; // 初始缩放为1
      this.initGlobalVar(1);
      const calcScale = () => {
        const screenWidth = window.innerWidth;
        const scale = screenWidth / designWidth; // 宽度缩放比例
        el.style.zoom = scale;
        this.updateGlobalVar(scale);
      };

      // 初始化并监听窗口变化
      calcScale();
      window.addEventListener("resize", calcScale);
      // 保存清理函数
      el._autofitCleanup = () => {
        window.removeEventListener("resize", calcScale);
        document.documentElement.style.removeProperty("--scale-ratio");
        el.style.zoom = "1"; // 恢复默认缩放
      };
    },
    initGlobalVar(initialScale) {
      let style = document.getElementById("global-scale-style");
      if (!style) {
        style = document.createElement("style");
        style.id = "global-scale-style"; // 添加唯一标识
        document.head.appendChild(style);
      }
      style.textContent = `:root { --scale-ratio: ${initialScale}; }`;
    },
    updateGlobalVar(newScale) {
      const style = document.getElementById("global-scale-style");
      if (style) {
        style.textContent = `:root { --scale-ratio: ${newScale}; }`;
      }
    },
    /**
     * 判断当前是否为高版本 Chrome(>127)
     * 高版本 Chrome 在 body 缩放后,getBoundingClientRect 返回的坐标未同步缩放,
     * 需要手动修正,否则点击、拖拽等位置会偏移。
     */
    isChromeHighVersion() {
      const ua = navigator.userAgent.toLowerCase();
      const chromeIndex = ua.indexOf("chrome");
      if (chromeIndex > -1) {
        const version = ua.substring(chromeIndex + 7);
        const majorVersion = parseInt(version.split(".")[0], 10);
        return majorVersion > 127;
      }
      return false;
    },
    zoomPlugin() {
      // 保存浏览器原生的 getBoundingClientRect 方法,后续需要用它计算缩放后的坐标
      const originalGetBoundingClientRect =
        Element.prototype.getBoundingClientRect;

      if (!this.isChromeHighVersion()) {
        return;
      }
      Element.prototype.getBoundingClientRect = function () {
        const rect = originalGetBoundingClientRect.call(this);
        const zoom = Number(document.body.style.zoom || 1);
        const newRect = new DOMRect(
          // 可自行修改
          rect.x / zoom,
          rect.y / zoom,
          rect.width / zoom,
          rect.height / zoom
        );
        return newRect;
      };
    },
    setBodyZoom() {
      this.$nextTick(() => {
        this.initAutofitByWidth(1920, 1080, "body");
        this.zoomPlugin();
      });
    },
    //等比缩放高度铺满
    refreshScale() {
      const bodyStyle = document.createElement("style");
      bodyStyle.innerHTML = `body{width:${this.resizeData.width}px; height:${this.resizeData.height}px!important;background-image: none;background-color: #000517 !important;overflow: hidden;}`;
      document.documentElement.firstElementChild.appendChild(bodyStyle);
      let docWidth = document.documentElement.clientWidth;
      let docHeight = document.documentElement.clientHeight;
      const designWidth = this.resizeData.width,
        designHeight = this.resizeData.height,
        heightRatio = docHeight / designHeight;
      document.body.style =
        "transform:scale(" +
        heightRatio +
        ");transform-origin:left top;margin-left: " +
        (docWidth - designWidth * heightRatio) / 2 +
        "px";
    },
    //// 等比缩放宽度铺满
    refreshScaleWidth() {
      const bodyStyle = document.createElement("style");
      bodyStyle.innerHTML = `body{width:${this.resizeData.width}px; height:${this.resizeData.height}px!important;background-image: none;background-color: #000517 !important;overflow: hidden;}`;
      document.documentElement.firstElementChild.appendChild(bodyStyle);
      let docWidth = document.documentElement.clientWidth;
      const designWidth = this.resizeData.width,
        widthRatio = docWidth / designWidth;
      document.body.style =
        "transform:scale(" + widthRatio + ");transform-origin:left top;";
    },
    //// 全屏铺满    根据宽度100% 高度压缩改变  或者高度100%  宽度压缩改变
    refreshScaleFullh() {
      const bodyStyle = document.createElement("style");
      bodyStyle.innerHTML = `body{width:${this.resizeData.width}px; height:${this.resizeData.height}px!important;background-image: none;background-color: #000517 !important;overflow: hidden;}`;
      document.documentElement.firstElementChild.appendChild(bodyStyle);
      let docWidth = document.documentElement.clientWidth;
      let docHeight = document.documentElement.clientHeight;

      const designWidth = this.resizeData.width,
        designHeight = this.resizeData.height,
        widthRatio = docWidth / designWidth,
        heightRatio = docHeight / designHeight;
      document.body.style =
        "transform:scale(" +
        widthRatio +
        "," +
        heightRatio +
        ");transform-origin:left top;";
    },
    //根据宽度100% 高度不压缩
    refreshScaleFull() {
      let baseWidth = document.documentElement.clientWidth;
      let baseHeight = document.documentElement.clientHeight;
      let appStyle = document.getElementById("app").style;
      let realRatio = baseWidth / baseHeight;
      let designRatio = this.resizeData.ratio;
      let scaleRate = baseWidth / this.resizeData.width;
      if (realRatio > designRatio) {
        scaleRate = baseHeight / this.resizeData.height;
      }
      appStyle.transformOrigin = "left top";
      appStyle.transform = `scale(${scaleRate}) translateX(0%)`;
      appStyle.width = `${baseWidth / scaleRate}px`;
      //然后高度 自适应  里面内容根据flex 跟百分比计算
    },
  },
};

b. element-zoom-bug.js内容

javascript 复制代码
//element-ui框架里面的模块
import Popper from "element-ui/lib/utils/popper";

const root = window;

// 重写此方法,以解决 body zoom 导致 ElementUI 所有 popper 相关元素错位的问题
// bug 产生原因:因为此方法依赖 `document.documentElement.clientWidth` 和 `document.documentElement.clientHeight`
// 而这两个值是 body 缩放之前的宽度和高度,换句话说,这两个值与 `zoom` 无关
// 所以解决方案的思路就是:重写此方法,将这两个值换成能有效获取实际宽高的方式即可
// 我的方案是换成了 `document.body.getBoundingClientRect()` 的 `width` 和 `height`
// 但这个逻辑依赖于 `body` 本身的宽高,所以请根据自己的情况换成更合适你的方案食用~
Popper.prototype._getBoundaries = function (data, padding, boundariesElement) {
  // NOTE: 1 DOM access here
  var boundaries = {};
  var width, height;
  if (boundariesElement === "window") {
    var body = root.document.body,
      html = root.document.documentElement;

    height = Math.max(
      body.scrollHeight,
      body.offsetHeight,
      html.clientHeight,
      html.scrollHeight,
      html.offsetHeight
    );
    width = Math.max(
      body.scrollWidth,
      body.offsetWidth,
      html.clientWidth,
      html.scrollWidth,
      html.offsetWidth
    );

    boundaries = {
      top: 0,
      right: width,
      bottom: height,
      left: 0,
    };
  } else if (boundariesElement === "viewport") {
    var offsetParent = getOffsetParent(this._popper);
    var scrollParent = getScrollParent(this._popper);
    var offsetParentRect = getOffsetRect(offsetParent);

    // Thanks the fucking native API, `document.body.scrollTop` & `document.documentElement.scrollTop`
    var getScrollTopValue = function getScrollTopValue(element) {
      return element == document.body
        ? Math.max(document.documentElement.scrollTop, document.body.scrollTop)
        : element.scrollTop;
    };
    var getScrollLeftValue = function getScrollLeftValue(element) {
      return element == document.body
        ? Math.max(
            document.documentElement.scrollLeft,
            document.body.scrollLeft
          )
        : element.scrollLeft;
    };

    // if the popper is fixed we don't have to substract scrolling from the boundaries
    var scrollTop =
      data.offsets.popper.position === "fixed"
        ? 0
        : getScrollTopValue(scrollParent);
    var scrollLeft =
      data.offsets.popper.position === "fixed"
        ? 0
        : getScrollLeftValue(scrollParent);

    boundaries = {
      top: 0 - (offsetParentRect.top - scrollTop),
      // right: root.document.documentElement.clientWidth - (offsetParentRect.left - scrollLeft),
      right:
        root.document.body.getBoundingClientRect().width -
        (offsetParentRect.left - scrollLeft),
      // bottom: root.document.documentElement.clientHeight - (offsetParentRect.top - scrollTop),
      bottom:
        root.document.body.getBoundingClientRect().height -
        (offsetParentRect.top - scrollTop),
      left: 0 - (offsetParentRect.left - scrollLeft),
    };
  } else {
    if (getOffsetParent(this._popper) === boundariesElement) {
      boundaries = {
        top: 0,
        left: 0,
        right: boundariesElement.clientWidth,
        bottom: boundariesElement.clientHeight,
      };
    } else {
      boundaries = getOffsetRect(boundariesElement);
    }
  }
  boundaries.left += padding;
  boundaries.right -= padding;
  boundaries.top = boundaries.top + padding;
  boundaries.bottom = boundaries.bottom - padding;
  return boundaries;
};

// 注:以下 function 为上面重写 function 的依赖,均完整复制自 Popper.js

/**
 * Returns the offset parent of the given element
 * @function
 * @ignore
 * @argument {Element} element
 * @returns {Element} offset parent
 */
function getOffsetParent(element) {
  // NOTE: 1 DOM access here
  var offsetParent = element.offsetParent;
  return offsetParent === root.document.body || !offsetParent
    ? root.document.documentElement
    : offsetParent;
}

/**
 * Returns the scrolling parent of the given element
 * @function
 * @ignore
 * @argument {Element} element
 * @returns {Element} offset parent
 */
function getScrollParent(element) {
  var parent = element.parentNode;

  if (!parent) {
    return element;
  }

  if (parent === root.document) {
    // Firefox puts the scrollTOp value on `documentElement` instead of `body`, we then check which of them is
    // greater than 0 and return the proper element
    if (root.document.body.scrollTop || root.document.body.scrollLeft) {
      return root.document.body;
    } else {
      return root.document.documentElement;
    }
  }

  // Firefox want us to check `-x` and `-y` variations as well
  if (
    ["scroll", "auto"].indexOf(getStyleComputedProperty(parent, "overflow")) !==
      -1 ||
    ["scroll", "auto"].indexOf(
      getStyleComputedProperty(parent, "overflow-x")
    ) !== -1 ||
    ["scroll", "auto"].indexOf(
      getStyleComputedProperty(parent, "overflow-y")
    ) !== -1
  ) {
    // If the detected scrollParent is body, we perform an additional check on its parentNode
    // in this way we'll get body if the browser is Chrome-ish, or documentElement otherwise
    // fixes issue #65
    return parent;
  }
  return getScrollParent(element.parentNode);
}

/**
 * Get CSS computed property of the given element
 * @function
 * @ignore
 * @argument {Eement} element
 * @argument {String} property
 */
function getStyleComputedProperty(element, property) {
  // NOTE: 1 DOM access here
  var css = root.getComputedStyle(element, null);
  return css[property];
}

/**
 * Get the position of the given element, relative to its offset parent
 * @function
 * @ignore
 * @param {Element} element
 * @return {Object} position - Coordinates of the element and its `scrollTop`
 */
function getOffsetRect(element) {
  var elementRect = {
    width: element.offsetWidth,
    height: element.offsetHeight,
    left: element.offsetLeft,
    top: element.offsetTop,
  };

  elementRect.right = elementRect.left + elementRect.width;
  elementRect.bottom = elementRect.top + elementRect.height;

  // position
  return elementRect;
}

/**
 * Get offsets to the popper
 * @method
 * @memberof Popper
 * @access private
 * @param {Element} popper - the popper element
 * @param {Element} reference - the reference element (the popper will be relative to this)
 * @returns {Object} An object containing the offsets which will be applied to the popper
 */
Popper.prototype._getOffsets = function (popper, reference, placement) {
  placement = placement.split("-")[0];
  var popperOffsets = {};

  popperOffsets.position = this.state.position;
  var isParentFixed = popperOffsets.position === "fixed";

  //
  // Get reference element position
  //
  var referenceOffsets = getOffsetRectRelativeToCustomParent(
    reference,
    getOffsetParent(popper),
    isParentFixed
  );

  //
  // Get popper sizes
  //
  var popperRect = getOuterSizes(popper);

  //
  // Compute offsets of popper
  //

  // depending by the popper placement we have to compute its offsets slightly differently
  if (["right", "left"].indexOf(placement) !== -1) {
    popperOffsets.top =
      referenceOffsets.top +
      referenceOffsets.height / 2 -
      popperRect.height / 2;
    if (placement === "left") {
      popperOffsets.left = referenceOffsets.left - popperRect.width;
    } else {
      popperOffsets.left = referenceOffsets.right;
    }
  } else {
    popperOffsets.left =
      referenceOffsets.left + referenceOffsets.width / 2 - popperRect.width / 2;
    if (placement === "top") {
      popperOffsets.top = referenceOffsets.top - popperRect.height;
    } else {
      popperOffsets.top = referenceOffsets.bottom;
    }
  }

  // Add width and height to our offsets object
  popperOffsets.width = popperRect.width;
  popperOffsets.height = popperRect.height;

  return {
    popper: popperOffsets,
    reference: referenceOffsets,
  };
};
/**
 * Given an element and one of its parents, return the offset
 * @function
 * @ignore
 * @param {HTMLElement} element
 * @param {HTMLElement} parent
 * @return {Object} rect
 */
function getOffsetRectRelativeToCustomParent(element, parent, fixed) {
  var elementRect = getBoundingClientRect(element);
  var parentRect = getBoundingClientRect(parent,fixed);

  if (fixed) {
    var scrollParent = getScrollParent(parent);
    parentRect.top += scrollParent.scrollTop;
    parentRect.bottom += scrollParent.scrollTop;
    parentRect.left += scrollParent.scrollLeft;
    parentRect.right += scrollParent.scrollLeft;
  }

  var rect = {
    top: elementRect.top - parentRect.top,
    left: elementRect.left - parentRect.left,
    bottom: elementRect.top - parentRect.top + elementRect.height,
    right: elementRect.left - parentRect.left + elementRect.width,
    width: elementRect.width,
    height: elementRect.height,
  };
  return rect;
}
/**
 * Get the outer sizes of the given element (offset size + margins)
 * @function
 * @ignore
 * @argument {Element} element
 * @returns {Object} object containing width and height properties
 */
function getOuterSizes(element) {
  // NOTE: 1 DOM access here
  var _display = element.style.display,
    _visibility = element.style.visibility;
  element.style.display = "block";
  element.style.visibility = "hidden";
  var calcWidthToForceRepaint = element.offsetWidth;

  // original method
  var styles = root.getComputedStyle(element);
  var x = parseFloat(styles.marginTop) + parseFloat(styles.marginBottom);
  var y = parseFloat(styles.marginLeft) + parseFloat(styles.marginRight);
  var result = {
    width: element.offsetWidth + y,
    height: element.offsetHeight + x,
  };

  // reset element styles
  element.style.display = _display;
  element.style.visibility = _visibility;
  return result;
}

function getBoundingClientRect(element,fixed) {
  var rect = element.getBoundingClientRect();

  // whether the IE version is lower than 11
  var isIE = navigator.userAgent.indexOf("MSIE") != -1;

  // fix ie document bounding top always 0 bug
  var rectTop =
    isIE && element.tagName === "HTML" ? -element.scrollTop : rect.top;
  var zoom = window.getComputedStyle(document.body).zoom;
  const visualtop = fixed?rectTop * zoom:rectTop; // 视觉左偏移(物理像素)
  return {
    left: rect.left,
    top: visualtop,
    right: rect.right,
    bottom: rect.bottom,
    width: rect.right - rect.left,
    height: rect.bottom - rectTop,
  };
}

二、在App.vue中引用,使用mixins全局注入。由于resizeMixins.js在created生命周期内调了this.setBodyZoom();这块只需要引入即可。

javascript 复制代码
<script>
import resizeMixins from '@/utils/resizeMixins';
export default {
mixins:[resizeMixins],
}
</script>


以上是项目中使用的zoom的情况,希望大家一键三连!

相关推荐
一 乐2 小时前
养老院信息|基于springboot + vue养老院信息管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端
澄江静如练_3 小时前
侦听器即watch
前端·javascript·vue.js
console.log('npc')3 小时前
vue3文件上传弹窗,图片pdf,word,结合预览kkview
前端·javascript·vue.js·pdf·word
BD_Marathon3 小时前
Router_路由传参
前端·javascript·vue.js
Dreamcatcher_AC3 小时前
前端面试高频13问
前端·javascript·vue.js
登山人在路上3 小时前
Vue中导出和导入
前端·javascript·vue.js
成为大佬先秃头4 小时前
渐进式JavaScript框架:Vue
开发语言·javascript·vue.js
C_心欲无痕4 小时前
vue3 - shallowReadonly浅层只读响应式对象
前端·javascript·vue.js
_Kayo_4 小时前
HTML 拖放API
前端·javascript·html