Garlic Garlic

Svelte从入门到精通——Alert组件之动画

发表于 阅读时长4分钟

动画

当我们点击关闭Alert组件时,会出现一段向上关闭的动画。我们使用svelte/easing配合transition指令来实现相似的动画。

<script>
  import { circInOut } from "svelte/easing";

  function slideUp(node, { duration = 300 }) {
    return {
      duration,
      css: (t, u) => {
        const data = circInOut(t);
        return `
          transform: scaleY(${data});
          transform-origin: 0% 0%;
          opacity: ${data};
        `
      },
    };
  }
</script>

然后修改html内容:

{#if !closed}
	<div class={alertCls} transition:slideUp on:outroend={animationEnd}>
	...
{/if}

添加最后一个api事件afterClose:

const animationEnd = () => {
    dispatch("afterClose");
};

在App.svelte中试验一下

<script>
  import Alert from "./Alert.svelte";
  import SvelteIcon from "./icon/SvelteIcon.svelte";

  const onClose = () => {
    console.log('onClose');
  }

  const afterClose = () => {
    console.log('afterClose');
  }
</script>

<Alert
  message="Success Tips"
  type="success"
  showIcon
  closable
  on:close={onClose}
  on:afterClose={afterClose}
>
  <SvelteIcon slot="icon" />
</Alert>
<br />
<Alert
  message="Info Tips"
  showIcon
  closable
/>

完整代码

最后附上完整代码:

<script>
  import { createEventDispatcher } from "svelte";
  import { circInOut } from "svelte/easing";
  import classNames from "classnames";
  import SuccessIcon from "./icon/SuccessIcon.svelte";
  import InfoIcon from "./icon/InfoIcon.svelte";
  import WarningIcon from "./icon/WarningIcon.svelte";
  import ErrorIcon from "./icon/ErrorIcon.svelte";
  import CloseIcon from "./icon/CloseIcon.svelte";

  export let type = "info"; // info | success | warning | error
  export let showIcon = false;
  export let banner = false;
  export let closable = undefined;
  export let closeText = undefined;
  export let message = undefined;
  export let description = undefined;

  function getPropsSlot(slots, props, prop = "default") {
    if (props && props[prop]) {
      return props[prop];
    }
    if (slots && slots[prop]) {
      return slots[prop];
    }
    return null;
  }

  function slideUp(node, { duration = 300 }) {
    return {
      duration,
      css: (t, u) => {
        const data = circInOut(t);
        return `
          transform: scaleY(${data});
          transform-origin: 0% 0%;
          opacity: ${data};
        `;
      },
    };
  }

  const dispatch = createEventDispatcher();
  const slots = $$slots;
  const props = $$props;

  let prefixCls = "ant-alert";
  let closed = false;
  let iconTheme = "filled";

  // banner模式默认有 Icon
  showIcon = banner && showIcon === false ? true : showIcon;
  // banner模式默认为警告
  type = banner && type === "info" ? "warning" : type;

  const closeTextData = getPropsSlot(slots, props, "closeText");
  const messageData = getPropsSlot(slots, props, "message");
  const descriptionData = getPropsSlot(slots, props, "description");
  const hasCloseTextSlot = !!slots?.closeText;
  const hasMessageSlot = !!slots?.message;
  const hasDescriptionSlot = !!slots?.description;
  const hasIconSlot = !!slots?.icon;

  if (closeTextData) {
    closable = true;
  }

  if (!!descriptionData) {
    iconTheme = "outlined";
  }

  $: alertCls = classNames(prefixCls, {
    [`${prefixCls}-${type}`]: true,
    [`${prefixCls}-no-icon`]: !showIcon,
    [`${prefixCls}-banner`]: banner,
    [`${prefixCls}-closable`]: closable,
    [`${prefixCls}-with-description`]: !!descriptionData,
  });

  $: icon = {
    success: SuccessIcon,
    info: InfoIcon,
    warning: WarningIcon,
    error: ErrorIcon,
  }[type];

  const handleClose = (e) => {
    closed = true;
    dispatch("close", e);
  };

  const animationEnd = () => {
    dispatch("afterClose");
  };
</script>

{#if !closed}
  <div class={alertCls} transition:slideUp on:outroend={animationEnd}>
    {#if showIcon}
      {#if hasIconSlot}
        <slot name="icon" />
      {:else}
        <i class={`anticon ${prefixCls}-icon`}>
          <svelte:component this={icon} theme={iconTheme} />
        </i>
      {/if}
    {/if}
    <div class={`${prefixCls}-content`}>
      {#if messageData}
        {#if hasMessageSlot}
          <slot name="message" />
        {:else}
          <span class={`${prefixCls}-message`}>{message}</span>
        {/if}
      {/if}
      {#if descriptionData}
        {#if hasDescriptionSlot}
          <slot name="description" />
        {:else}
          <span class={`${prefixCls}-description`}>{description}</span>
        {/if}
      {/if}
    </div>
    {#if closable}
      <a
        href={""}
        on:click|preventDefault={handleClose}
        class={`${prefixCls}-close-icon`}
        role="button"
        tabindex="0"
      >
        {#if hasCloseTextSlot}
          <slot name="closeText" />
        {:else if closeTextData}
          {closeText}
        {:else}
          <i class={`anticon anticon-close ${prefixCls}-icon`}>
            <CloseIcon />
          </i>
        {/if}
      </a>
    {/if}
  </div>
{/if}

<style lang="less">
  @import "./alert.less";
</style>

小结

本章我们完成了:

  • transition:fn指令的使用
  • 自定义过渡效果
  • 完整的代码演示