You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3+Vuetify中CSS动画无法超出v-card/v-col边界问题求助

问题描述

我正在构建一个书籍收藏应用,使用Vue3搭配Vuetify,在网格布局中设置了多个卡片。目前给通知图标添加了圆形扩散动画,但动画在触及v-card或v-col的边界时会被截断,希望动画能够超出边界显示。

书籍卡片上被截断的通知动画

用户提供的原始代码:

<template>
  <v-card class="rounded-0 test" width="300px">
    <v-img
      :src="img"
      gradient="to bottom, rgba(0,0,0,.1), rgba(0,0,0,.8)"
      cover
    >
      <v-container class="fill-height" fluid>
        <v-row v-if="newestVols" class="align-self-start full-width">
          <v-spacer></v-spacer>
          <v-col cols="3">
            <v-btn
              class="icon info-icon"
              variant="plain"
              color="red"
              icon="mdi-exclamation-thick"
              :ripple="false"
            >
            </v-btn>
          </v-col>
        </v-row>
        <v-row class="align-self-end full-width">
          <v-col cols="12">
            <v-card-title
              class="text-white pointer"
              @click="dialog = true"
              style="cursor: pointer"
            >
              {{ title }}

              <v-dialog v-model="dialog" width="auto">
                <v-card>
                  <v-container fluid>
                    <v-row justify="center">
                      <v-col
                        v-for="vol in sortedArray"
                        :key="vol.id"
                        class="d-flex justify-center fill-height"
                      >
                        <BookCard
                          :title="title"
                          :cover="vol.cover"
                          :number="vol.number"
                          :id="vol.id"
                          :editionID="id"
                          :showRemoveButton="true"
                        />
                      </v-col>
                    </v-row>
                  </v-container>
                  <v-card-actions>
                    <v-btn color="primary" block @click="dialog = false"
                      >Close Dialog</v-btn
                    >
                  </v-card-actions>
                </v-card>
              </v-dialog>
            </v-card-title>
          </v-col>
        </v-row>
      </v-container>
    </v-img>
  </v-card>
</template>

<script>
import BookCard from "./BookCard.vue";

export default {
  props: {
    img: {
      type: String,
    },
    title: {
      type: String,
    },
    volumes: {
      type: Array,
      default: [],
    },
    id: {
      type: Number,
    },
    volumes: {
      type: Array,
      default: [],
    },
  },
  components: {
    BookCard,
  },
  data: () => ({
    dialog: false,
  }),
  computed: {
    sortedArray() {
      return this.volumes.sort((a, b) => (a.number > b.number ? -1 : 1));
    },
    newestVols() {
      return ["test"];
    },
  },
};
</script>

<style>
.icon {
  font-size: 30px !important;
  opacity: 1 !important;
}

.full-width {
  width: 100%;
}

.pointer {
  cursor: pointer;
}

.info-icon i {
  animation: pulse 2s ease-out infinite;
  border-radius: 50% !important;
}

@keyframes pulse {
  0% {
    box-shadow: 0 0 0 0px rgb(255, 0, 0, 1), 0 0 0 0px rgba(255, 44, 44, 0.85);
  }
  100% {
    box-shadow: 0 0 0 15px rgba(255, 0, 0, 0), 0 0 0 30px rgba(255, 44, 44, 0);
  }
}
</style>
解决方案

动画被截断的核心原因是Vuetify的v-card、v-img、v-col等组件默认设置了overflow: hidden,超出边界的内容会被裁剪。解决步骤如下:

1. 给父级容器添加允许溢出的样式

定义一个自定义类,覆盖默认的溢出裁剪规则:

.allow-overflow {
  overflow: visible !important;
}

然后在模板中,给动画元素的所有父级容器(包括v-col、v-container、v-img、v-card)添加这个类,确保动画能突破边界显示。

2. 调整动画层级(可选)

如果动画被其他卡片遮挡,给动画图标添加更高的层级,确保它显示在最上层:

.info-icon i {
  position: relative;
  z-index: 10;
}

修改后的完整代码

<template>
  <v-card class="rounded-0 test allow-overflow" width="300px">
    <v-img
      :src="img"
      gradient="to bottom, rgba(0,0,0,.1), rgba(0,0,0,.8)"
      cover
      class="allow-overflow"
    >
      <v-container class="fill-height allow-overflow" fluid>
        <v-row v-if="newestVols" class="align-self-start full-width">
          <v-spacer></v-spacer>
          <v-col cols="3" class="allow-overflow">
            <v-btn
              class="icon info-icon"
              variant="plain"
              color="red"
              icon="mdi-exclamation-thick"
              :ripple="false"
            >
            </v-btn>
          </v-col>
        </v-row>
        <v-row class="align-self-end full-width">
          <v-col cols="12">
            <v-card-title
              class="text-white pointer"
              @click="dialog = true"
              style="cursor: pointer"
            >
              {{ title }}

              <v-dialog v-model="dialog" width="auto">
                <v-card>
                  <v-container fluid>
                    <v-row justify="center">
                      <v-col
                        v-for="vol in sortedArray"
                        :key="vol.id"
                        class="d-flex justify-center fill-height"
                      >
                        <BookCard
                          :title="title"
                          :cover="vol.cover"
                          :number="vol.number"
                          :id="vol.id"
                          :editionID="id"
                          :showRemoveButton="true"
                        />
                      </v-col>
                    </v-row>
                  </v-container>
                  <v-card-actions>
                    <v-btn color="primary" block @click="dialog = false"
                      >Close Dialog</v-btn
                    >
                  </v-card-actions>
                </v-card>
              </v-dialog>
            </v-card-title>
          </v-col>
        </v-row>
      </v-container>
    </v-img>
  </v-card>
</template>

<script>
import BookCard from "./BookCard.vue";

export default {
  props: {
    img: {
      type: String,
    },
    title: {
      type: String,
    },
    volumes: {
      type: Array,
      default: [],
    },
    id: {
      type: Number,
    },
  },
  components: {
    BookCard,
  },
  data: () => ({
    dialog: false,
  }),
  computed: {
    sortedArray() {
      return this.volumes.sort((a, b) => (a.number > b.number ? -1 : 1));
    },
    newestVols() {
      return ["test"];
    },
  },
};
</script>

<style>
.icon {
  font-size: 30px !important;
  opacity: 1 !important;
}

.full-width {
  width: 100%;
}

.pointer {
  cursor: pointer;
}

.allow-overflow {
  overflow: visible !important;
}

.info-icon i {
  animation: pulse 2s ease-out infinite;
  border-radius: 50% !important;
  position: relative;
  z-index: 10;
}

@keyframes pulse {
  0% {
    box-shadow: 0 0 0 0px rgb(255, 0, 0, 1), 0 0 0 0px rgba(255, 44, 44, 0.85);
  }
  100% {
    box-shadow: 0 0 0 15px rgba(255, 0, 0, 0), 0 0 0 30px rgba(255, 44, 44, 0);
  }
}
</style>

内容的提问来源于stack exchange,提问作者Myriad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 19:24:59