视效
视效
为元素增添动态的色彩效果,例如指针和卡片上的彩色光效。
为元素添加折射效果,折射的位置将受到光标位置的影响。
视效
视效
为元素增添动态的色彩效果,例如指针和卡片上的彩色光效。
为元素添加折射效果,折射的位置将受到光标位置的影响。
视效
视效
为元素增添动态的色彩效果,例如指针和卡片上的彩色光效。
为元素添加折射效果,折射的位置将受到光标位置的影响。
A modern clock app icon featuring an analog clock face with black hour markers and hands on a white background. The icon has a gradient border transitioning from coral pink to purple blue, with rounded corners and a 3D effect.

Framer 中的真实数据:时钟

2022年11月1日

尽管 Framer 已经成为了主打网页设计的建站工具,但我们依旧能够使用它进行原型设计。这个系列的文章将会利用 Framer 的 Override 特性引入一些真实数据到 Framer 中,在第一篇中,我们将利用简单的代码设计一个真实的时钟组件。

创建UI

首先,我们需要创建时钟的UI图层,它至少包含时针、分针与秒针。这里以 iOS 的时钟组件为例,你可以直接复制下面的组件到 Framer 来加速这一进程。

在制作指针时,我们将指针的部分放在一个正方形 Frame 中,这会方便我们接下来的操作。

design-clock

Override

在 Framer 中新建一个 Override 文件,然后分别创建名为 Hour、Minute 和 Second 的函数组件。

export function Hour(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
            />
        )
    }
}
export function Minute(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
            />
        )
    }
}
export function Second(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
            />
        )
    }
}

然后将三个Override组件分别连接到 UI 中的时针、分针、秒针中。

link-override

使用Date()

JS 提供的 Date 对象让我们可以非常方便地获取当前时间。我们利用这一功能分别获取当前的时、分、秒以及毫秒数据。为了方便使用这些数据,我们将其定义为“数字”。

const hour = Number(new Date().getHours())
const minute = Number(new Date().getMinutes())
const second = Number(new Date().getSeconds())
const millisecond = Number(new Date().getMilliseconds())

从秒针开始

我们的目标是让秒钟拥有平滑的转动效果(就像 iOS 那样),而 Framer Motion 能够为我们达成这一效果。首先,我们创建名为 “from” 与 “to” 的 Variants,设置 initial 为 “from”,设置 animate 为“to”。

export function Second(Component): ComponentType {
    return (props) => {
        const variant = {
            from: {
            },
            to: {
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}

“from” 代表了我们的秒针将从什么角度开始转动,它取决于当前时间中的秒。秒针在时钟上每秒转动6度,因此我们在 “from” 中设置角度为 6 \* second。

    from: {
                rotate: 6 * second,
            },

现在,我们的秒针就有了一个起始角度。但它每次只会以0 °、6 °、12 °……作为起点,为了让角度更加精确,我们加入毫秒作为参考。

    from: {
                rotate: 6 * second + 6 * (millisecond / 1000)
            },

这样,我们的秒针就有了一个较为精准的起点。接下来,我们为秒针设置动画的终点 “to”,只需在起点的基础加上360°即可。此外我们要将缓动曲线设置为线性,持续时间为60秒,无限重复。

    to: {
                rotate: 6 * second + 6 * (millisecond / 1000) + 360,
                transition: { duration: 60, ease: "linear", repeat: Infinity },
            },

现在我们可以看到秒针已经完美的工作了。

分针与时针

对于分针与时针,我们使用相同的方法。对于分针的起始角度,我们将 “分钟(minute)” 与 “秒(second)” 结合,持续时间设置为 3600 秒(一小时)。

        const variant = {
            from: {
                rotate: 6 * minute + 6 * (second / 60),
            },
            to: {
                rotate: 6 * minute + 6 * (second / 60) + 360,
                transition: {
                    duration: 3600,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }

而时针只需使用“时(hour)” 与 “分钟(minute)”即可。在使用 hour 数据前,我们需要对数据进行一个换算,因为 hour 是以24小时格式进行计算的,而时针上的数字最大只有12。当时针进行到中午12点时(hour = 12),我们需要让 hour 返回到0,这样时针才可以正常地工作。这里只需一个简单的判断即可。

 const hour12 = hour >= 12 ? hour - 12 : hour

然后同样的为时针添加 Variant 参数,需要注意的是我们要将动画的持续时间设置为43200秒(12小时)。

        const hour12 = hour >= 12 ? hour - 12 : hour
        const variant = {
            from: {
                rotate: 30 * hour12 + 30 * (minute / 60),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 30 * hour12 + 30 * (minute / 60) + 360,
                transition: {
                    duration: 43200,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }

完整代码

import type { ComponentType } from "react"
const hour = Number(new Date().getHours())
const minute = Number(new Date().getMinutes())
const second = Number(new Date().getSeconds())
const millisecond = Number(new Date().getMilliseconds())
export function Hour(Component): ComponentType {
    return (props) => {
        const hour12 = hour >= 12 ? hour - 12 : hour
        const variant = {
            from: {
                rotate: 30 * hour12 + 30 * (minute / 60),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 30 * hour12 + 30 * (minute / 60) + 360,
                transition: {
                    duration: 43200,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}
export function Minute(Component): ComponentType {
    return (props) => {
        const variant = {
            from: {
                rotate: 6 * minute + 6 * (second / 60),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 6 * minute + 6 * (second / 60) + 360,
                transition: {
                    duration: 3600,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}
export function Second(Component): ComponentType {
    return (props) => {
        const variant = {
            from: {
                rotate: 6 * second + 6 * (millisecond / 1000),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 6 * second + 6 * (millisecond / 1000) + 360,
                transition: { duration: 60, ease: "linear", repeat: Infinity },
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}

现在,所有的指针都开始正常工作了。这是我目前利用 Framer Motion 的 Variants 特性制作的动画,可能在某些数据方面会出现准确性问题,如果你有更好地解决方案,欢迎一起交流。

Content

聊一个
聊一个
A modern clock app icon featuring an analog clock face with black hour markers and hands on a white background. The icon has a gradient border transitioning from coral pink to purple blue, with rounded corners and a 3D effect.

Framer 中的真实数据:时钟

2022年11月1日

尽管 Framer 已经成为了主打网页设计的建站工具,但我们依旧能够使用它进行原型设计。这个系列的文章将会利用 Framer 的 Override 特性引入一些真实数据到 Framer 中,在第一篇中,我们将利用简单的代码设计一个真实的时钟组件。

创建UI

首先,我们需要创建时钟的UI图层,它至少包含时针、分针与秒针。这里以 iOS 的时钟组件为例,你可以直接复制下面的组件到 Framer 来加速这一进程。

在制作指针时,我们将指针的部分放在一个正方形 Frame 中,这会方便我们接下来的操作。

design-clock

Override

在 Framer 中新建一个 Override 文件,然后分别创建名为 Hour、Minute 和 Second 的函数组件。

export function Hour(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
            />
        )
    }
}
export function Minute(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
            />
        )
    }
}
export function Second(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
            />
        )
    }
}

然后将三个Override组件分别连接到 UI 中的时针、分针、秒针中。

link-override

使用Date()

JS 提供的 Date 对象让我们可以非常方便地获取当前时间。我们利用这一功能分别获取当前的时、分、秒以及毫秒数据。为了方便使用这些数据,我们将其定义为“数字”。

const hour = Number(new Date().getHours())
const minute = Number(new Date().getMinutes())
const second = Number(new Date().getSeconds())
const millisecond = Number(new Date().getMilliseconds())

从秒针开始

我们的目标是让秒钟拥有平滑的转动效果(就像 iOS 那样),而 Framer Motion 能够为我们达成这一效果。首先,我们创建名为 “from” 与 “to” 的 Variants,设置 initial 为 “from”,设置 animate 为“to”。

export function Second(Component): ComponentType {
    return (props) => {
        const variant = {
            from: {
            },
            to: {
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}

“from” 代表了我们的秒针将从什么角度开始转动,它取决于当前时间中的秒。秒针在时钟上每秒转动6度,因此我们在 “from” 中设置角度为 6 \* second。

    from: {
                rotate: 6 * second,
            },

现在,我们的秒针就有了一个起始角度。但它每次只会以0 °、6 °、12 °……作为起点,为了让角度更加精确,我们加入毫秒作为参考。

    from: {
                rotate: 6 * second + 6 * (millisecond / 1000)
            },

这样,我们的秒针就有了一个较为精准的起点。接下来,我们为秒针设置动画的终点 “to”,只需在起点的基础加上360°即可。此外我们要将缓动曲线设置为线性,持续时间为60秒,无限重复。

    to: {
                rotate: 6 * second + 6 * (millisecond / 1000) + 360,
                transition: { duration: 60, ease: "linear", repeat: Infinity },
            },

现在我们可以看到秒针已经完美的工作了。

分针与时针

对于分针与时针,我们使用相同的方法。对于分针的起始角度,我们将 “分钟(minute)” 与 “秒(second)” 结合,持续时间设置为 3600 秒(一小时)。

        const variant = {
            from: {
                rotate: 6 * minute + 6 * (second / 60),
            },
            to: {
                rotate: 6 * minute + 6 * (second / 60) + 360,
                transition: {
                    duration: 3600,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }

而时针只需使用“时(hour)” 与 “分钟(minute)”即可。在使用 hour 数据前,我们需要对数据进行一个换算,因为 hour 是以24小时格式进行计算的,而时针上的数字最大只有12。当时针进行到中午12点时(hour = 12),我们需要让 hour 返回到0,这样时针才可以正常地工作。这里只需一个简单的判断即可。

 const hour12 = hour >= 12 ? hour - 12 : hour

然后同样的为时针添加 Variant 参数,需要注意的是我们要将动画的持续时间设置为43200秒(12小时)。

        const hour12 = hour >= 12 ? hour - 12 : hour
        const variant = {
            from: {
                rotate: 30 * hour12 + 30 * (minute / 60),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 30 * hour12 + 30 * (minute / 60) + 360,
                transition: {
                    duration: 43200,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }

完整代码

import type { ComponentType } from "react"
const hour = Number(new Date().getHours())
const minute = Number(new Date().getMinutes())
const second = Number(new Date().getSeconds())
const millisecond = Number(new Date().getMilliseconds())
export function Hour(Component): ComponentType {
    return (props) => {
        const hour12 = hour >= 12 ? hour - 12 : hour
        const variant = {
            from: {
                rotate: 30 * hour12 + 30 * (minute / 60),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 30 * hour12 + 30 * (minute / 60) + 360,
                transition: {
                    duration: 43200,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}
export function Minute(Component): ComponentType {
    return (props) => {
        const variant = {
            from: {
                rotate: 6 * minute + 6 * (second / 60),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 6 * minute + 6 * (second / 60) + 360,
                transition: {
                    duration: 3600,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}
export function Second(Component): ComponentType {
    return (props) => {
        const variant = {
            from: {
                rotate: 6 * second + 6 * (millisecond / 1000),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 6 * second + 6 * (millisecond / 1000) + 360,
                transition: { duration: 60, ease: "linear", repeat: Infinity },
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}

现在,所有的指针都开始正常工作了。这是我目前利用 Framer Motion 的 Variants 特性制作的动画,可能在某些数据方面会出现准确性问题,如果你有更好地解决方案,欢迎一起交流。

Content

聊一个
聊一个
A modern clock app icon featuring an analog clock face with black hour markers and hands on a white background. The icon has a gradient border transitioning from coral pink to purple blue, with rounded corners and a 3D effect.

Framer 中的真实数据:时钟

2022年11月1日

尽管 Framer 已经成为了主打网页设计的建站工具,但我们依旧能够使用它进行原型设计。这个系列的文章将会利用 Framer 的 Override 特性引入一些真实数据到 Framer 中,在第一篇中,我们将利用简单的代码设计一个真实的时钟组件。

创建UI

首先,我们需要创建时钟的UI图层,它至少包含时针、分针与秒针。这里以 iOS 的时钟组件为例,你可以直接复制下面的组件到 Framer 来加速这一进程。

在制作指针时,我们将指针的部分放在一个正方形 Frame 中,这会方便我们接下来的操作。

design-clock

Override

在 Framer 中新建一个 Override 文件,然后分别创建名为 Hour、Minute 和 Second 的函数组件。

export function Hour(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
            />
        )
    }
}
export function Minute(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
            />
        )
    }
}
export function Second(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
            />
        )
    }
}

然后将三个Override组件分别连接到 UI 中的时针、分针、秒针中。

link-override

使用Date()

JS 提供的 Date 对象让我们可以非常方便地获取当前时间。我们利用这一功能分别获取当前的时、分、秒以及毫秒数据。为了方便使用这些数据,我们将其定义为“数字”。

const hour = Number(new Date().getHours())
const minute = Number(new Date().getMinutes())
const second = Number(new Date().getSeconds())
const millisecond = Number(new Date().getMilliseconds())

从秒针开始

我们的目标是让秒钟拥有平滑的转动效果(就像 iOS 那样),而 Framer Motion 能够为我们达成这一效果。首先,我们创建名为 “from” 与 “to” 的 Variants,设置 initial 为 “from”,设置 animate 为“to”。

export function Second(Component): ComponentType {
    return (props) => {
        const variant = {
            from: {
            },
            to: {
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}

“from” 代表了我们的秒针将从什么角度开始转动,它取决于当前时间中的秒。秒针在时钟上每秒转动6度,因此我们在 “from” 中设置角度为 6 \* second。

    from: {
                rotate: 6 * second,
            },

现在,我们的秒针就有了一个起始角度。但它每次只会以0 °、6 °、12 °……作为起点,为了让角度更加精确,我们加入毫秒作为参考。

    from: {
                rotate: 6 * second + 6 * (millisecond / 1000)
            },

这样,我们的秒针就有了一个较为精准的起点。接下来,我们为秒针设置动画的终点 “to”,只需在起点的基础加上360°即可。此外我们要将缓动曲线设置为线性,持续时间为60秒,无限重复。

    to: {
                rotate: 6 * second + 6 * (millisecond / 1000) + 360,
                transition: { duration: 60, ease: "linear", repeat: Infinity },
            },

现在我们可以看到秒针已经完美的工作了。

分针与时针

对于分针与时针,我们使用相同的方法。对于分针的起始角度,我们将 “分钟(minute)” 与 “秒(second)” 结合,持续时间设置为 3600 秒(一小时)。

        const variant = {
            from: {
                rotate: 6 * minute + 6 * (second / 60),
            },
            to: {
                rotate: 6 * minute + 6 * (second / 60) + 360,
                transition: {
                    duration: 3600,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }

而时针只需使用“时(hour)” 与 “分钟(minute)”即可。在使用 hour 数据前,我们需要对数据进行一个换算,因为 hour 是以24小时格式进行计算的,而时针上的数字最大只有12。当时针进行到中午12点时(hour = 12),我们需要让 hour 返回到0,这样时针才可以正常地工作。这里只需一个简单的判断即可。

 const hour12 = hour >= 12 ? hour - 12 : hour

然后同样的为时针添加 Variant 参数,需要注意的是我们要将动画的持续时间设置为43200秒(12小时)。

        const hour12 = hour >= 12 ? hour - 12 : hour
        const variant = {
            from: {
                rotate: 30 * hour12 + 30 * (minute / 60),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 30 * hour12 + 30 * (minute / 60) + 360,
                transition: {
                    duration: 43200,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }

完整代码

import type { ComponentType } from "react"
const hour = Number(new Date().getHours())
const minute = Number(new Date().getMinutes())
const second = Number(new Date().getSeconds())
const millisecond = Number(new Date().getMilliseconds())
export function Hour(Component): ComponentType {
    return (props) => {
        const hour12 = hour >= 12 ? hour - 12 : hour
        const variant = {
            from: {
                rotate: 30 * hour12 + 30 * (minute / 60),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 30 * hour12 + 30 * (minute / 60) + 360,
                transition: {
                    duration: 43200,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}
export function Minute(Component): ComponentType {
    return (props) => {
        const variant = {
            from: {
                rotate: 6 * minute + 6 * (second / 60),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 6 * minute + 6 * (second / 60) + 360,
                transition: {
                    duration: 3600,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}
export function Second(Component): ComponentType {
    return (props) => {
        const variant = {
            from: {
                rotate: 6 * second + 6 * (millisecond / 1000),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 6 * second + 6 * (millisecond / 1000) + 360,
                transition: { duration: 60, ease: "linear", repeat: Infinity },
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}

现在,所有的指针都开始正常工作了。这是我目前利用 Framer Motion 的 Variants 特性制作的动画,可能在某些数据方面会出现准确性问题,如果你有更好地解决方案,欢迎一起交流。

Content

聊一个
聊一个
A modern clock app icon featuring an analog clock face with black hour markers and hands on a white background. The icon has a gradient border transitioning from coral pink to purple blue, with rounded corners and a 3D effect.

Framer 中的真实数据:时钟

2022年11月1日

尽管 Framer 已经成为了主打网页设计的建站工具,但我们依旧能够使用它进行原型设计。这个系列的文章将会利用 Framer 的 Override 特性引入一些真实数据到 Framer 中,在第一篇中,我们将利用简单的代码设计一个真实的时钟组件。

创建UI

首先,我们需要创建时钟的UI图层,它至少包含时针、分针与秒针。这里以 iOS 的时钟组件为例,你可以直接复制下面的组件到 Framer 来加速这一进程。

在制作指针时,我们将指针的部分放在一个正方形 Frame 中,这会方便我们接下来的操作。

design-clock

Override

在 Framer 中新建一个 Override 文件,然后分别创建名为 Hour、Minute 和 Second 的函数组件。

export function Hour(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
            />
        )
    }
}
export function Minute(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
            />
        )
    }
}
export function Second(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
            />
        )
    }
}

然后将三个Override组件分别连接到 UI 中的时针、分针、秒针中。

link-override

使用Date()

JS 提供的 Date 对象让我们可以非常方便地获取当前时间。我们利用这一功能分别获取当前的时、分、秒以及毫秒数据。为了方便使用这些数据,我们将其定义为“数字”。

const hour = Number(new Date().getHours())
const minute = Number(new Date().getMinutes())
const second = Number(new Date().getSeconds())
const millisecond = Number(new Date().getMilliseconds())

从秒针开始

我们的目标是让秒钟拥有平滑的转动效果(就像 iOS 那样),而 Framer Motion 能够为我们达成这一效果。首先,我们创建名为 “from” 与 “to” 的 Variants,设置 initial 为 “from”,设置 animate 为“to”。

export function Second(Component): ComponentType {
    return (props) => {
        const variant = {
            from: {
            },
            to: {
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}

“from” 代表了我们的秒针将从什么角度开始转动,它取决于当前时间中的秒。秒针在时钟上每秒转动6度,因此我们在 “from” 中设置角度为 6 \* second。

    from: {
                rotate: 6 * second,
            },

现在,我们的秒针就有了一个起始角度。但它每次只会以0 °、6 °、12 °……作为起点,为了让角度更加精确,我们加入毫秒作为参考。

    from: {
                rotate: 6 * second + 6 * (millisecond / 1000)
            },

这样,我们的秒针就有了一个较为精准的起点。接下来,我们为秒针设置动画的终点 “to”,只需在起点的基础加上360°即可。此外我们要将缓动曲线设置为线性,持续时间为60秒,无限重复。

    to: {
                rotate: 6 * second + 6 * (millisecond / 1000) + 360,
                transition: { duration: 60, ease: "linear", repeat: Infinity },
            },

现在我们可以看到秒针已经完美的工作了。

分针与时针

对于分针与时针,我们使用相同的方法。对于分针的起始角度,我们将 “分钟(minute)” 与 “秒(second)” 结合,持续时间设置为 3600 秒(一小时)。

        const variant = {
            from: {
                rotate: 6 * minute + 6 * (second / 60),
            },
            to: {
                rotate: 6 * minute + 6 * (second / 60) + 360,
                transition: {
                    duration: 3600,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }

而时针只需使用“时(hour)” 与 “分钟(minute)”即可。在使用 hour 数据前,我们需要对数据进行一个换算,因为 hour 是以24小时格式进行计算的,而时针上的数字最大只有12。当时针进行到中午12点时(hour = 12),我们需要让 hour 返回到0,这样时针才可以正常地工作。这里只需一个简单的判断即可。

 const hour12 = hour >= 12 ? hour - 12 : hour

然后同样的为时针添加 Variant 参数,需要注意的是我们要将动画的持续时间设置为43200秒(12小时)。

        const hour12 = hour >= 12 ? hour - 12 : hour
        const variant = {
            from: {
                rotate: 30 * hour12 + 30 * (minute / 60),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 30 * hour12 + 30 * (minute / 60) + 360,
                transition: {
                    duration: 43200,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }

完整代码

import type { ComponentType } from "react"
const hour = Number(new Date().getHours())
const minute = Number(new Date().getMinutes())
const second = Number(new Date().getSeconds())
const millisecond = Number(new Date().getMilliseconds())
export function Hour(Component): ComponentType {
    return (props) => {
        const hour12 = hour >= 12 ? hour - 12 : hour
        const variant = {
            from: {
                rotate: 30 * hour12 + 30 * (minute / 60),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 30 * hour12 + 30 * (minute / 60) + 360,
                transition: {
                    duration: 43200,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}
export function Minute(Component): ComponentType {
    return (props) => {
        const variant = {
            from: {
                rotate: 6 * minute + 6 * (second / 60),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 6 * minute + 6 * (second / 60) + 360,
                transition: {
                    duration: 3600,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}
export function Second(Component): ComponentType {
    return (props) => {
        const variant = {
            from: {
                rotate: 6 * second + 6 * (millisecond / 1000),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 6 * second + 6 * (millisecond / 1000) + 360,
                transition: { duration: 60, ease: "linear", repeat: Infinity },
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}

现在,所有的指针都开始正常工作了。这是我目前利用 Framer Motion 的 Variants 特性制作的动画,可能在某些数据方面会出现准确性问题,如果你有更好地解决方案,欢迎一起交流。

Content

聊一个
聊一个
A modern clock app icon featuring an analog clock face with black hour markers and hands on a white background. The icon has a gradient border transitioning from coral pink to purple blue, with rounded corners and a 3D effect.

Framer 中的真实数据:时钟

2022年11月1日

尽管 Framer 已经成为了主打网页设计的建站工具,但我们依旧能够使用它进行原型设计。这个系列的文章将会利用 Framer 的 Override 特性引入一些真实数据到 Framer 中,在第一篇中,我们将利用简单的代码设计一个真实的时钟组件。

创建UI

首先,我们需要创建时钟的UI图层,它至少包含时针、分针与秒针。这里以 iOS 的时钟组件为例,你可以直接复制下面的组件到 Framer 来加速这一进程。

在制作指针时,我们将指针的部分放在一个正方形 Frame 中,这会方便我们接下来的操作。

design-clock

Override

在 Framer 中新建一个 Override 文件,然后分别创建名为 Hour、Minute 和 Second 的函数组件。

export function Hour(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
            />
        )
    }
}
export function Minute(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
            />
        )
    }
}
export function Second(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
            />
        )
    }
}

然后将三个Override组件分别连接到 UI 中的时针、分针、秒针中。

link-override

使用Date()

JS 提供的 Date 对象让我们可以非常方便地获取当前时间。我们利用这一功能分别获取当前的时、分、秒以及毫秒数据。为了方便使用这些数据,我们将其定义为“数字”。

const hour = Number(new Date().getHours())
const minute = Number(new Date().getMinutes())
const second = Number(new Date().getSeconds())
const millisecond = Number(new Date().getMilliseconds())

从秒针开始

我们的目标是让秒钟拥有平滑的转动效果(就像 iOS 那样),而 Framer Motion 能够为我们达成这一效果。首先,我们创建名为 “from” 与 “to” 的 Variants,设置 initial 为 “from”,设置 animate 为“to”。

export function Second(Component): ComponentType {
    return (props) => {
        const variant = {
            from: {
            },
            to: {
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}

“from” 代表了我们的秒针将从什么角度开始转动,它取决于当前时间中的秒。秒针在时钟上每秒转动6度,因此我们在 “from” 中设置角度为 6 \* second。

    from: {
                rotate: 6 * second,
            },

现在,我们的秒针就有了一个起始角度。但它每次只会以0 °、6 °、12 °……作为起点,为了让角度更加精确,我们加入毫秒作为参考。

    from: {
                rotate: 6 * second + 6 * (millisecond / 1000)
            },

这样,我们的秒针就有了一个较为精准的起点。接下来,我们为秒针设置动画的终点 “to”,只需在起点的基础加上360°即可。此外我们要将缓动曲线设置为线性,持续时间为60秒,无限重复。

    to: {
                rotate: 6 * second + 6 * (millisecond / 1000) + 360,
                transition: { duration: 60, ease: "linear", repeat: Infinity },
            },

现在我们可以看到秒针已经完美的工作了。

分针与时针

对于分针与时针,我们使用相同的方法。对于分针的起始角度,我们将 “分钟(minute)” 与 “秒(second)” 结合,持续时间设置为 3600 秒(一小时)。

        const variant = {
            from: {
                rotate: 6 * minute + 6 * (second / 60),
            },
            to: {
                rotate: 6 * minute + 6 * (second / 60) + 360,
                transition: {
                    duration: 3600,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }

而时针只需使用“时(hour)” 与 “分钟(minute)”即可。在使用 hour 数据前,我们需要对数据进行一个换算,因为 hour 是以24小时格式进行计算的,而时针上的数字最大只有12。当时针进行到中午12点时(hour = 12),我们需要让 hour 返回到0,这样时针才可以正常地工作。这里只需一个简单的判断即可。

 const hour12 = hour >= 12 ? hour - 12 : hour

然后同样的为时针添加 Variant 参数,需要注意的是我们要将动画的持续时间设置为43200秒(12小时)。

        const hour12 = hour >= 12 ? hour - 12 : hour
        const variant = {
            from: {
                rotate: 30 * hour12 + 30 * (minute / 60),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 30 * hour12 + 30 * (minute / 60) + 360,
                transition: {
                    duration: 43200,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }

完整代码

import type { ComponentType } from "react"
const hour = Number(new Date().getHours())
const minute = Number(new Date().getMinutes())
const second = Number(new Date().getSeconds())
const millisecond = Number(new Date().getMilliseconds())
export function Hour(Component): ComponentType {
    return (props) => {
        const hour12 = hour >= 12 ? hour - 12 : hour
        const variant = {
            from: {
                rotate: 30 * hour12 + 30 * (minute / 60),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 30 * hour12 + 30 * (minute / 60) + 360,
                transition: {
                    duration: 43200,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}
export function Minute(Component): ComponentType {
    return (props) => {
        const variant = {
            from: {
                rotate: 6 * minute + 6 * (second / 60),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 6 * minute + 6 * (second / 60) + 360,
                transition: {
                    duration: 3600,
                    ease: "linear",
                    repeat: Infinity,
                },
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}
export function Second(Component): ComponentType {
    return (props) => {
        const variant = {
            from: {
                rotate: 6 * second + 6 * (millisecond / 1000),
                transition: { duration: 0, ease: "linear" },
            },
            to: {
                rotate: 6 * second + 6 * (millisecond / 1000) + 360,
                transition: { duration: 60, ease: "linear", repeat: Infinity },
            },
        }
        return (
            <Component
                {...props}
                variants={variant}
                initial={"from"}
                animate={"to"}
            />
        )
    }
}

现在,所有的指针都开始正常工作了。这是我目前利用 Framer Motion 的 Variants 特性制作的动画,可能在某些数据方面会出现准确性问题,如果你有更好地解决方案,欢迎一起交流。

Content

聊一个
聊一个

@2025 THE STAGE FOR JAY JI.

  •  
  • 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  •  
  • 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

@2025 THE STAGE FOR JAY JI.

  •  
  • 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  •  
  • 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

@2025 THE STAGE FOR JAY JI.

  •  
  • 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  •  
  • 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

@2025 THE STAGE FOR JAY JI.

  •  
  • 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  •  
  • 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

@2025 THE STAGE FOR JAY JI.

  •  
  • 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  •  
  • 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9