From 50ee7232f3c98fe24a1f2e31dba9508cfec14887 Mon Sep 17 00:00:00 2001
From: gjj <Ganjj@probim.com.cn>
Date: Fri, 21 Feb 2025 17:35:39 +0800
Subject: [PATCH] 模型预览

---
 src/components/TransitionSize.vue |  253 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 253 insertions(+), 0 deletions(-)

diff --git a/src/components/TransitionSize.vue b/src/components/TransitionSize.vue
new file mode 100644
index 0000000..5f76e7d
--- /dev/null
+++ b/src/components/TransitionSize.vue
@@ -0,0 +1,253 @@
+<template>
+  <transition
+    :name="name"
+    @before-appear="beforeAppear"
+    @appear="appear"
+    @after-appear="afterAppear"
+    @appear-cancelled="appearCancelled"
+    @before-enter="beforeEnter"
+    @enter="enter"
+    @after-enter="afterEnter"
+    @enter-cancelled="enterCancelled"
+    @before-leave="beforeLeave"
+    @leave="leave"
+    @after-leave="afterLeave"
+    @leave-cancelled="leaveCancelled"
+  >
+    <slot></slot>
+  </transition>
+</template>
+
+<script>
+  export default {
+    name: "CollapseTransition",
+    props: {
+      name: {
+        type: String,
+        required: false,
+        default: "collapse",
+      },
+      dimension: {
+        type: String,
+        required: false,
+        default: "height",
+        validator: (value) => {
+          return ["height", "width"].includes(value);
+        },
+      },
+      duration: {
+        type: Number,
+        required: false,
+        default: 300,
+      },
+      easing: {
+        type: String,
+        required: false,
+        default: "ease-in-out",
+      },
+    },
+    data() {
+      return {
+        cachedStyles: null,
+      };
+    },
+    computed: {
+      transition() {
+        let transitions = [];
+        Object.keys(this.cachedStyles).forEach((key) => {
+          transitions.push(`${this.convertToCssProperty(key)} ${this.duration}ms ${this.easing}`);
+        });
+        return transitions.join(", ");
+      },
+    },
+    watch: {
+      dimension() {
+        this.clearCachedDimensions();
+      },
+    },
+    methods: {
+      beforeAppear(el) {
+        // Emit the event to the parent
+        this.$emit("before-appear", el);
+      },
+      appear(el) {
+        // Emit the event to the parent
+        this.$emit("appear", el);
+      },
+      afterAppear(el) {
+        // Emit the event to the parent
+        this.$emit("after-appear", el);
+      },
+      appearCancelled(el) {
+        // Emit the event to the parent
+        this.$emit("appear-cancelled", el);
+      },
+      beforeEnter(el) {
+        // Emit the event to the parent
+        this.$emit("before-enter", el);
+      },
+      enter(el, done) {
+        // Because width and height may be 'auto',
+        // first detect and cache the dimensions
+        this.detectAndCacheDimensions(el);
+        // The order of applying styles is important:
+        // - 1. Set styles for state before transition
+        // - 2. Force repaint
+        // - 3. Add transition style
+        // - 4. Set styles for state after transition
+        // If the order is not right and you open any 2nd level submenu
+        // for the first time, the transition will not work.
+        this.setClosedDimensions(el);
+        this.hideOverflow(el);
+        this.forceRepaint(el);
+        this.setTransition(el);
+        this.setOpenedDimensions(el);
+        // Emit the event to the parent
+        this.$emit("enter", el, done);
+        // Call done() when the transition ends
+        // to trigger the @after-enter event.
+        setTimeout(done, this.duration);
+      },
+      afterEnter(el) {
+        // Clean up inline styles
+        this.unsetOverflow(el);
+        this.unsetTransition(el);
+        this.unsetDimensions(el);
+        this.clearCachedDimensions();
+        // Emit the event to the parent
+        this.$emit("after-enter", el);
+      },
+      enterCancelled(el) {
+        // Emit the event to the parent
+        this.$emit("enter-cancelled", el);
+      },
+      beforeLeave(el) {
+        // Emit the event to the parent
+        this.$emit("before-leave", el);
+      },
+      leave(el, done) {
+        // For some reason, @leave triggered when starting
+        // from open state on page load. So for safety,
+        // check if the dimensions have been cached.
+        this.detectAndCacheDimensions(el);
+        // The order of applying styles is less important
+        // than in the enter phase, as long as we repaint
+        // before setting the closed dimensions.
+        // But it is probably best to use the same
+        // order as the enter phase.
+        this.setOpenedDimensions(el);
+        this.hideOverflow(el);
+        this.forceRepaint(el);
+        this.setTransition(el);
+        this.setClosedDimensions(el);
+        // Emit the event to the parent
+        this.$emit("leave", el, done);
+        // Call done() when the transition ends
+        // to trigger the @after-leave event.
+        // This will also cause v-show
+        // to reapply 'display: none'.
+        setTimeout(done, this.duration);
+      },
+      afterLeave(el) {
+        // Clean up inline styles
+        this.unsetOverflow(el);
+        this.unsetTransition(el);
+        this.unsetDimensions(el);
+        this.clearCachedDimensions();
+        // Emit the event to the parent
+        this.$emit("after-leave", el);
+      },
+      leaveCancelled(el) {
+        // Emit the event to the parent
+        this.$emit("leave-cancelled", el);
+      },
+      detectAndCacheDimensions(el) {
+        // Cache actual dimensions
+        // only once to void invalid values when
+        // triggering during a transition
+        if (this.cachedStyles) return;
+        const visibility = el.style.visibility;
+        const display = el.style.display;
+        // Trick to get the width and
+        // height of a hidden element
+        el.style.visibility = "hidden";
+        el.style.display = "";
+        this.cachedStyles = this.detectRelevantDimensions(el);
+        // Restore any original styling
+        el.style.visibility = visibility;
+        el.style.display = display;
+      },
+      clearCachedDimensions() {
+        this.cachedStyles = null;
+      },
+      detectRelevantDimensions(el) {
+        // These properties will be transitioned
+        if (this.dimension === "height") {
+          return {
+            height: el.offsetHeight + "px",
+            paddingTop: el.style.paddingTop || this.getCssValue(el, "padding-top"),
+            paddingBottom: el.style.paddingBottom || this.getCssValue(el, "padding-bottom"),
+          };
+        }
+        if (this.dimension === "width") {
+          return {
+            width: el.offsetWidth + "px",
+            paddingLeft: el.style.paddingLeft || this.getCssValue(el, "padding-left"),
+            paddingRight: el.style.paddingRight || this.getCssValue(el, "padding-right"),
+          };
+        }
+        return {};
+      },
+      setTransition(el) {
+        el.style.transition = this.transition;
+      },
+      unsetTransition(el) {
+        el.style.transition = "";
+      },
+      hideOverflow(el) {
+        el.style.overflow = "hidden";
+      },
+      unsetOverflow(el) {
+        el.style.overflow = "";
+      },
+      setClosedDimensions(el) {
+        Object.keys(this.cachedStyles).forEach((key) => {
+          el.style[key] = "0";
+        });
+      },
+      setOpenedDimensions(el) {
+        Object.keys(this.cachedStyles).forEach((key) => {
+          el.style[key] = this.cachedStyles[key];
+        });
+      },
+      unsetDimensions(el) {
+        Object.keys(this.cachedStyles).forEach((key) => {
+          el.style[key] = "";
+        });
+      },
+      forceRepaint(el) {
+        // Force repaint to make sure the animation is triggered correctly.
+        // Thanks: https://markus.oberlehner.net/blog/transition-to-height-auto-with-vue/
+        getComputedStyle(el)[this.dimension];
+      },
+      getCssValue(el, style) {
+        return getComputedStyle(el, null).getPropertyValue(style);
+      },
+      convertToCssProperty(style) {
+        // Example: convert 'paddingTop' to 'padding-top'
+        // Thanks: https://gist.github.com/tan-yuki/3450323
+        const upperChars = style.match(/([A-Z])/g);
+        if (!upperChars) {
+          return style;
+        }
+        for (let i = 0, n = upperChars.length; i < n; i++) {
+          style = style.replace(new RegExp(upperChars[i]), "-" + upperChars[i].toLowerCase());
+        }
+        if (style.slice(0, 1) === "-") {
+          style = style.slice(1);
+        }
+        return style;
+      },
+    },
+  };
+</script>

--
Gitblit v1.9.3