【问题标题】:Can we restrict CSS keyframe animations to a scope我们可以将 CSS 关键帧动画限制在一个范围内吗
【发布时间】:2018-09-15 01:13:38
【问题描述】:

我想知道是否可以将关键帧动画限制在基于类名的范围内。好处是能够多次使用相同的动画名称而不会出现问题。我找不到任何关于它的信息..

如果不可能: 是否有处理命名冲突的最佳实践?

【问题讨论】:

  • 我曾经使用 SCSS 之类的东西为我的关键帧生成自动创建的名称。它们可能没有描述性,但它们确保了唯一性。像$animation-id-count: 0 !default; @function animation-id{ $animation-id-count: $animation-id-count + 1; @return animation-id-#{$animation-id-count}; } 这样的东西,然后像.class { $id: animation-id(); @keyframes #{$id}{ ...keyframes } }; animation: $id 1s infinite; 一样使用它。这样,如果您将它插入 SCSS 中的任何其他位置或移动它,它仍然会匹配正确的动画。
  • 这是一种处理命名问题的有趣方式——刚刚做了一个测试实现,效果很好。尝试遵循 itcss 结构,我希望找到一种方法,可以将所有动画放在一个额外的 scss 文件中。那是不可能的,但非常感谢您的建议。必须考虑走这条路:)
  • @somethinghere 看起来我想做的范围界定是不可能的。您想重写您的评论作为答案并提供一个完整的简短示例吗?顺便说一句,在函数内部增加 $animation-id-count 时,我需要在行尾添加一个 !global ,否则每次调用都会返回相同的(初始)值。如果您想设置答案,请告诉我 - 否则我可以使用示例代码添加答案。但你当然应该得到荣誉!
  • 我已将其添加为答案。我正在考虑这个问题,我猜如果你能找到一种跟踪命名空间的方法,你可以使用一个函数来确定要返回的动画名称,所以这是一个开始。另外,关于!global,我用过!default,但确实是错误的。

标签: css scope css-animations


【解决方案1】:

我曾经使用 SCSS 之类的东西来为我的关键帧生成自动创建的名称。它们可能没有描述性,但它们确保了唯一性。比如:

$animation-id-count: 0 !global;

@function animation-id {

  $animation-id-count: $animation-id-count + 1;
  @return animation-id-#{$animation-id-count};

}

在此之后,只需像这样在代码中使用该函数:

.class {

  $id: animation-id();

  @keyframes #{$id}{
    ...keyframes
  }

  animation: $id 1s infinite;

}

这样,如果您将它插入到 SCSS 中的任何其他位置或移动它,它仍然会匹配正确的动画,并且它会阻止命名空间以任何方式重叠。

【讨论】:

  • 感谢您的回答!
  • 这不能回答问题
  • @zaqx “如果不可能:是否有任何最佳实践来处理命名冲突?” - 这是不可能的,所以这里是如何避免命名冲突。 回答问题
  • @somethinghere "我们可以将 CSS 关键帧动画限制在一个范围内吗" — 大文本的页面顶部。它甚至在 URL 中。
【解决方案2】:

这是一种 JSX 方法(为此您需要 object-hash)。

以下示例展示了如何根据transform: scale(n) 定义具有各自唯一ID 的不同动画。为此,定义一个返回关键帧及其 ID 的函数。关键帧 ID 是一个自定义字符串,后缀为函数选项的哈希值(例如比例因子)。

(注意 CSS 自定义标识符,例如,不要在您的 ID 中包含 .。请参阅 MDN: < custom-ident >。)

import hash from "object-hash";

const keyFramesScale = (options = {}) => {

    let { transforms, id, scale } = options;
    transforms = transforms || "";
    scale = scale || 1.25;

    const keyFramesId = `scale${id ? "-" + id : ""}-${hash(options).substring(0, 6)}`;
    const keyFrames = {
        [`@keyframes ${keyFramesId}`]: {
            "100%": {
                transform: `scale(${scale}) ${transforms}`,
            },
            "0%": {
                transform: `scale(1) ${transforms}`,
            }
        }
    };

    return [keyFramesId, keyFrames];
};

使用方法:

const [scaleUpId, keyFramesScaleUp] = keyFramesScale({ scale: 1.25, transforms: "rotate(-30deg)", id: "up" });
const [scaleDownId, keyFramesScaleDown] = keyFramesScale({ scale: 0.75, transforms: "rotate(-30deg)", id: "down" });

// scaleUpId = "scale-up-c61254"
// scaleDownId = "scale-down-6194d5"
// ...

<tag style={{
    ...keyFramesScaleUp,
    ...keyFramesScaleDown,
    ...(!hasTouchScreen && isActive && !isClicked && {
        animation: `${scaleUpId} 0.5s infinite alternate linear`,
        "&:hover": {
            animation: "none",
        },
    }),
    ...(isClicked && {
        animation: `${scaleDownId} .25s 1 linear`,
    }),
}} />

当然,您可以编写一个更通用的函数,对整个关键帧进行哈希处理,并据此为其分配一个 ID。

编辑

为了具体化已经说过的话,这里是通用方法。我们首先定义一个通用函数,它接受动画名称(例如scalepulse 等)、关键帧(可以是对象或函数)以及可选的关键帧参数及其默认值。

import hash from "object-hash";

const createKeyFramesId = (id, keyFrames) => {
    return `${id}-${hash(keyFrames).substring(0, 6)}`;
};

const genericKeyFrames = (name, keyFrames, defaults = {}, options = {}) => {

    if (typeof keyFrames === "function") {
        // The order of defaults & options is important: the latter overrides the former.
        keyFrames = keyFrames({ ...defaults, ...options });
    }

    const keyFramesId = createKeyFramesId(name, keyFrames);
    const keyFramesObject = {
        [`@keyframes ${keyFramesId}`]: keyFrames
    };
    return [keyFramesId, keyFramesObject];
};

从现在开始,我们可以定义各种动画。用法同上。

export const keyFramesPulse = () =>
    genericKeyFrames("pulse", {
        "100%": {
            opacity: "1",
        },
        "0%": {
            opacity: "0.5",
        },
    });

export const keyFramesRotate = (options = {}) => {
    const defaults = {
        rotate: 360,
        transforms: "",
    };

    const rotateKeyFrames = ({ rotate, transforms }) => {
        return {
            "100%": {
                transform: `rotate(${rotate}deg) ${transforms}`,
            }
        }
    };

    return genericKeyFrames(`rotate`, rotateKeyFrames, defaults, options);
};

export const keyFramesScale = (options = {}) => {

    const defaults = {
        scale: 1.25,
        transforms: ""
    };

    const scaleKeyFrames = ({ scale, transforms }) => {
        return {
            "100%": {
                transform: `scale(${scale}) ${transforms}`,
            },
            "0%": {
                transform: `scale(1) ${transforms}`,
            }
        }
    };

    return genericKeyFrames(`scale`, scaleKeyFrames, defaults, options);
};

它在 DevTools 中的样子:

【讨论】:

  • 嗨,Firmin Martin,尽管我一直在寻找一种 css/scss 范围界定方式 - 感谢您抽出宝贵时间分享您的 JS 方法!对于我的项目类型,它看起来有点“开销”,但对于 JS 应用程序来说,一旦它们变得更大,它肯定是有意义的!不错的帖子,写得很好 - 谢谢!
  • @Luckyfella 谢谢!我在使用 React + MUI (material-ui) 时提出了这种方法。刚刚意识到我错误地说这是一个“JSS”解决方案:它是一个“JSX”解决方案。我想这个解决方案的主要优点是关键帧 ID 是人类可读的。
猜你喜欢
  • 2015-09-20
  • 2016-11-23
  • 1970-01-01
  • 1970-01-01
  • 2023-03-26
  • 1970-01-01
  • 2012-11-14
  • 1970-01-01
相关资源
最近更新 更多