0

我遇到了过渡组的问题。问题是我正在尝试渲染按某些条件过滤的列表。在我的示例中 - 取决于最小/最大界限。

如何修复?

最初,我尝试使用在转换组内呈现的数组的计算值。但显然,每次更新范围滑块值时,它都会返回新数组。因此,我尝试使用@input事件处理程序来更新列表,该列表仅在需要更新时才呈现。最让我困惑的是我试图为@enter @enter-to @leave @leave-to事件设置转换组挂钩并且它们被正确触发。就一次。我也尝试过使其由 JS 驱动,但即使我设置了:css="false" :duration="500".

我做了一个简单的例子来说明这个问题。码沙盒.io

我期望的是列表(数组)得到更新,然后它触发转换组内容重新渲染。并执行 FLIP 动画。但问题是,如果用作过滤条件的值快速更改(例如,用户使用鼠标拖动范围滑块)动画要么根本不执行,要么导致滞后。从左到右拖动范围滑块(动画滞后)时准确观察行为,它与从右到左拖动它时看到的不同(第一个元素不执行动画)。另请注意,如果您单击滑块设置值 - 动画将按预期执行。

4

1 回答 1

0

参考:“并且执行 FLIP 动画” ...

考虑到您要翻转该项目,我根本不会使用 a <transition-group>
我会使用 3d 旋转效果(我猜这是你想要的)并使用计算属性控制旋转角度,基于项目道具和范围值之间的关系。

这里是:

Vue.config.devtools = false;
Vue.config.productionTip = false;

Vue.component('rotatingButton', {
  template: `
    <div>
      <div class="flipper">
        <div
          class="items"
          :style="{ transform: \`rotateX(\${rotation}deg)\` }">
          <div v-for="item in items"
            :key="item.ID"
            :class="[item.value, 'item']"
            v-text="item.value" />
        </div>
      </div>
      <input type="range" v-model="range" min="0" max="1500">
      <div class="color-range">
        <div v-for="item in items"
          :style="{width: \`\${(item.max - item.min)/15}%\`, 
                   left: \`\${item.min/15}%\`,
                   backgroundColor: item.barColor }"
        />
      </div>
    </div>`,
  data: () => ({
    range: 100,
    items: [{
          ID: 0,
          min: 0,
          max: 500,
          value: "first",
          barColor: '#369'
        }, {
          ID: 1,
          min: 500,
          max: 700,
          value: "second",
          barColor: "#900"         
        }, {
          ID: 2,
          min: 700,
          max: 1500,
          value: "third",
          barColor: "#393"
        }
      ],
  }),
  computed: {
    rotation() {
      return this.range < this.items[0].max ? 0 : (this.range < this.items[1].max ? 90 : 180);
    }
  }
})

new Vue({
  el: '#app'
});
#app {
  text-align: center;
  margin-top: 60px;
}
.flipper {
  perspective: 210px;
  height: 30px;
  width: 150px;
  line-height: 30px;
  margin: 15px auto;
}
.items {
  height: 100%;
  perspective-origin: 150% 150%;
  transform-style: preserve-3d;
  transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.item {
  display: block;
  position: absolute;
  width: 100%;
  height: 100%;
  border: 1px solid rgba(33,66,99,.33);
  font: 25px monospace;
  color: rgba(33,66,99,.33);
  background-color: white;
}

.first { transform: translateZ(15px); }
.second { transform: rotateX(-90deg) translateZ(15px); }
.third { transform: rotateX(180deg) translateZ(15px); }
.fourth { transform: rotateX(90deg) translateZ(15px); }
.color-range {
  padding: 0 1rem;
  position: relative;
  height: 4px;
}
input[type="range"] {width: 100%; margin: 0;}
.color-range > div {
  position: absolute;
  top: 0;
  height: 100%;
  opacity: .65;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <rotating-button />
</div>


如果需要,可以扩展该示例以处理更多项目:

Vue.config.devtools = false;
Vue.config.productionTip = false;

Vue.component('rotatingButton', {
  template: `
    <div>
      <div class="flipper">
        <div
          class="items"
          :style="{ transform: \`rotateX(\${rotation}deg)\` }">
          <div v-for="(item, key) in items"
          	v-show="range > key -1 && range < key + 3"
            :key="key"
            :class="['item', \`key-\${key}\`]"
            v-text="item + 1" />
        </div>
      </div>
      <div class="inputs">
        <input type="range" v-model="range" min="1" :max="max" step=".01">
        <input type="number" v-model="max">
      </div>
      <pre>{{logger}}</pre>
    </div>`,
  data: () => ({
    range: 10,
    max: 15,
  }),
  computed: {
    items() {
      return _.times(this.max);
    },
    rotation() {
      return Math.floor(((Number(this.range) * 9) + 36) * 1e3) / 1e2;
    },
    logger() {
      return JSON.stringify({
        range: Number(this.range),
        items: Number(this.max),
        rotation: this.rotation
      }, true, 2);
    }
  },
})

new Vue({
  el: '#app'
});
#app {
  text-align: center;
}

.flipper {
  perspective: 210px;
  height: 30px;
  width: 150px;
  line-height: 30px;
  margin: 15px auto;
}

.items {
  height: 100%;
  perspective-origin: 150% 150%;
  transform-style: preserve-3d;
  transition: transform 0.1s ease-in-out;
}

.item {
  display: block;
  position: absolute;
  width: 100%;
  height: 100%;
  border: 1px solid rgba(33, 66, 99, .42);
  font: 25px monospace;
  color: rgba(33, 66, 99, .42);
  background-color: white;
  transform-style: preserve-3d;
}

.item {
  transform: translateZ(15px);
}
.inputs {
  display: flex;
  padding: 0 1rem;
}
input[type="range"] {
  flex-grow: 1;
  margin: 0 1rem 0 0;
}
input[type="number"] {
  font-size: 1.5rem;
  width: 100px;
}
.item:nth-child(4n + 1) {
  transform: rotateX(-90deg) translateZ(15px);
}

.item:nth-child(4n + 2) {
  transform: rotateX(180deg) translateZ(15px);
}

.item:nth-child(4n + 3) {
  transform: rotateX(90deg) translateZ(15px);
}


.color-range>div {
  position: absolute;
  top: 0;
  height: 100%;
  opacity: .65;
}

pre {
  text-align: left;
  background-color: #def;
  border: 1px solid #ccc;
  border-radius: .25rem;
  padding: 1rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <rotating-button />
</div>

于 2019-05-16T01:07:51.877 回答