在QWIK应用程序中构建制定运动动画
#javascript #网络开发人员 #教程 #qwik

动画是您可以添加到网站的最酷的东西之一,以使其使其流行,并且与其他队列不同。

您可以使用CSS进行大多数动画,但是您必须了解很多才能获得良好的结果。

这就是为什么许多React生态系统中的许多人都喜欢Framer Motion

它具有清晰,声明和简洁的API,这使得在网络上构建动画是一种乐趣。

使用框架运动的优点

Framer Motion是一个强大的动画库,可以在网页上创建平滑而美丽的动画。以下是使用Framer Motion的一些优点:

  • 用户友好的:Framer Motion具有直观的API,即使是针对动画的新手,也可以轻松创建动画。
  • 声明性:框架运动使用声明语法,阐明了动画中正在发生的事情。
  • 可自定义:Framer Motion提供了广泛的可自定义选项,可帮助您创建适合您特定需求的动画。
  • 性能:Framer Motion已针对性能进行了优化,因此您可以创建复杂的动画而不必担心放慢网站。
  • 社区:Framer Motion拥有一个庞大而活跃的社区,可提供支持和资源,以帮助您充分利用图书馆。

与Qwik集成

ð§您可以在GitHub上的这篇文章中找到代码。

我以前写过关于how to run React inside Qwik的文章,但在这里是tldr;复习:

  1. 启动一个新的Qwik应用程序:pnpm create qwik@latest
  2. 添加反应集成:pnpm run qwik add react

然后我们可以添加framer-motion

pnpm install framer-motion

现在我们可以用framer-motion编写一个反应组件。

创建Qwikified的组件

这很简单。可以说我们有此代码:

import { motion } from "framer-motion";

const MyComponent = () => {
  return (
    <motion.div
      animate={{
      scale: [1, 2, 2, 1, 1],
      rotate: [0, 0, 270, 270, 0],
      borderRadius: ['20%', '20%', '50%', '50%', '20%'],
      backgroundColor: ['#ff008c', '#d309e1', '#9c1aff', '#7700ff', '#ff008c'],
      transition: { duration: 2 },
    }}
      className="h-52 w-52 rounded bg-green-500"
    />
  );
};

我们需要做的一切qwikify是:

// FILE: src/integrations/react/framer.tsx
// ==========================================

// 👇🏽 this tells Qwik that the JSX here is React
/** @jsxImportSource react */

import { motion } from "framer-motion";

// one function to import 
import { qwikify$ } from '@builder.io/qwik-react';

const MyComponent = () => (
  <motion.div
    animate={{
      scale: [1, 2, 2, 1, 1],
      rotate: [0, 0, 270, 270, 0],
      borderRadius: ['20%', '20%', '50%', '50%', '20%'],
      backgroundColor: ['#ff008c', '#d309e1', '#9c1aff', '#7700ff', '#ff008c'],
      transition: { duration: 2 },
    }}
    className="h-52 w-52 rounded bg-green-500"
  />
);

// All you need is to export:
export const FramerQwik = qwikify$(MyComponent);

在我们的好老朋友Github Copilot的帮助下,让我们分解:

  1. @builder.io/qwik-react导入qwikify$函数。
  2. framer-motion导入motion组件。
  3. 创建一个返回div元素的MyComponent功能组件。
  4. animate prop传递给motion.div元素。
  5. 将对象作为包含动画属性的动画道具的值。
  6. className prop传递给motion.div元素。
  7. 使用qwikify$函数导出组件。

然后我们可以在Qwik应用中使用它:

// FILE: src/routes/index.tsx
// ==========================================

import { component$ } from '@builder.io/qwik';
import { FramerQwik } from '~/integrations/react/framer';

export default component$(() => {
  return (
    <div class="flex flex-col gap-4">
      <h1 class="text-3xl">Qwik/React Framer Motion</h1>
      <div class="grid place-content-center">
        <FramerQwik client:idle />
      </div>
    </div>
  );
});

注意,在上面的示例中,我们使用client:idle,这是何时补充反应组件的指令。这告诉Qwik等待浏览器idle事件,然后水合物反应,导致Qwik海洋中的一个反应岛。

更复杂的东西怎么样?

Qwik内部的React Framer Mover图像库

让我们以framer-motion文档的示例并将其转换为qwik组件。

我喜欢这个小图片库:

让我们创建一个新文件:src/integrations/react/image-gallery.tsx

我们将所有内容都放在一个文件中

/** @jsxImportSource react */

import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { wrap } from 'popmotion';
import { qwikify$ } from '@builder.io/qwik-react';

const images = [
  'https://d33wubrfki0l68.cloudfront.net/dd23708ebc4053551bb33e18b7174e73b6e1710b/dea24/static/images/wallpapers/shared-colors@2x.png',
  'https://d33wubrfki0l68.cloudfront.net/49de349d12db851952c5556f3c637ca772745316/cfc56/static/images/wallpapers/bridge-02@2x.png',
  'https://d33wubrfki0l68.cloudfront.net/594de66469079c21fc54c14db0591305a1198dd6/3f4b1/static/images/wallpapers/bridge-01@2x.png',
];

const variants = {
  enter: (direction: number) => {
    return {
      x: direction > 0 ? 1000 : -1000,
      opacity: 0,
    };
  },
  center: {
    zIndex: 1,
    x: 0,
    opacity: 1,
  },
  exit: (direction: number) => {
    return {
      zIndex: 0,
      x: direction < 0 ? 1000 : -1000,
      opacity: 0,
    };
  },
};

const swipeConfidenceThreshold = 10000;
const swipePower = (offset: number, velocity: number) => {
  return Math.abs(offset) * velocity;
};

export const Example = () => {
  const [[page, direction], setPage] = useState([0, 0]);

  const imageIndex = wrap(0, images.length, page);

  const paginate = (newDirection: number) => {
    setPage([page + newDirection, newDirection]);
  };

  return (
    <div className='framer-gallery'>
      <AnimatePresence initial={false} custom={direction}>
        <motion.img
          key={page}
          src={images[imageIndex]}
          custom={direction}
          variants={variants}
          initial="enter"
          animate="center"
          exit="exit"
          transition={{
            x: { type: 'spring', stiffness: 300, damping: 30 },
            opacity: { duration: 0.2 },
          }}
          drag="x"
          dragConstraints={{ left: 0, right: 0 }}
          dragElastic={1}
          onDragEnd={(e, { offset, velocity }) => {
            const swipe = swipePower(offset.x, velocity.x);

            if (swipe < -swipeConfidenceThreshold) {
              paginate(1);
            } else if (swipe > swipeConfidenceThreshold) {
              paginate(-1);
            }
          }}
        />
      </AnimatePresence>
      <div className="next" onClick={() => paginate(1)}>
        {''}
      </div>
      <div className="prev" onClick={() => paginate(-1)}>
        {''}
      </div>
    </div>
  );
};

export const ImageGallery = qwikify$(Example);

在这一点上,我们要做的就是导入src/routes/index.tsx中的组件并决定如何加载它:

// FILE: src/routes/index.tsx
// ==========================================

import { component$ } from '@builder.io/qwik';
import { FramerQwik } from '~/integrations/react/framer';
import { ImageGallery } from '~/integrations/react/image-gallery';

export default component$(() => {
  return (
    <div class="flex flex-col gap-4">
      <h1 class="text-3xl">Qwik/React Framer Motion</h1>
      <div class="grid place-content-center">
        <FramerQwik client:idle />
      </div>
      <ImageGallery client:visible />
    </div>
  );
});

要注意的一件事是与原始代码和原始框的微小区别在于,我将片段(<>)更改为

使用className只是为了使样式更容易。否则,在组件的情况下,我们添加了client:visible加载策略,才能在视图中看到画廊时进行水合。

这就是它的样子:

一切都起作用!

这里最酷的部分之一(至少对书呆子með)是画廊的代码仅在视图时执行。

检查一下(注意网络选项卡):

奖金:运动'替代framer-motion

使用framer-motion很酷,但它具有进口反应和水合的成本。

如果我告诉您有一个动画库,它的性能与制定者一样,重量较小,并且具有非常相似的API?

另外,它是由构建framer-motion(和PosePopmotion)的同一个人创建的。

我在谈论Motion One

这不是React库,而是用于性能的香草JS动画库,其捆绑面积非常低为3.8kb。

运动一个人具有与框架相同的铃铛和哨子,与固体和vue的集成以及dedicated devtools

需要我说更多?

Qwik运动一个例子

尽管这没有Qwik SDK,但让我们看看如何添加基本动画。

由于此库是纯JS,因此我们可以使用Qwik方法useVisibleTask(),仅在浏览器中运行此代码,如下示例:

import { component$, useVisibleTask$ } from '@builder.io/qwik';
import { animate } from 'motion';

export default component$(() => {
  useVisibleTask$(() => {
    animate(
      '#animation-target',
      {
        scale: [1, 2, 2, 1, 1],
        rotate: [0, 0, 270, 270, 0],
        borderRadius: ['20%', '20%', '50%', '50%', '20%'],
        backgroundColor: [
          '#ff008c',
          '#d309e1',
          '#9c1aff',
          '#7700ff',
          '#ff008c',
        ],
      },
      {
        duration: 2,
        easing: 'ease-in-out',
        repeat: 2,
        direction: 'alternate',
      }
    );
  });
  return (
    <div
      id="animation-target"
      // Some tailwind styling for sizing and initial color
      class="m-auto mt-24 w-52 h-52 bg-slate-500"
    ></div>
  );
});

我们对动画使用了相同的值,与framer-motion示例中。但是,我们将值作为对象传递。

还注意到,animate函数的第三个参数是选项所在的位置,与framer不同的是,那里的transition键在animate Prop中传递。

结论

在这篇文章中,我展示了我们如何在qwik应用程序中同时使用framer-motionMotion One

在我看来,使用强大的动画库是一个巨大的胜利。

如果您已经使用framer-motion,那么仅添加您可能已经在React应用程序中构建的相同动画就几乎没有摩擦。

拥有此选项可能有助于在性能和美丽运动设计的情况下逐步采用Qwik。

视觉上与您的组件构建

Builder.io是一个视觉CMS,可让您使用your components在网站上创建内容。

Try it out Learn more

Builder.io blog上阅读full post