javascript - 在没有 Jquery 的情况下,在 mouseEnter 和 mouseLeave 上 react 停止/开始淡出

标签 javascript css reactjs

我正在尝试将错误消息显示为 toast (React 组件),该消息将在几秒钟后淡出。然而,当用户在淡出时将鼠标悬停在 toast 上时,淡出应该停止并且 toast 应该恢复到其初始状态,当他将鼠标悬停在 toast 上时,淡出应该再次开始.它可以通过像这样使用 JQuery 来实现 -

//函数在时间 - t 秒后开始淡出

static  fadeOutToast(id, t) {
        let toast = document.getElementById(id);
        if (toast) {
          setTimeout(() => {
                   FadeAndRemove('#' + id);
          }, t * 1000);
         }
      }

/** * t1 - 淡出动画的时间 */

 static FadeAndRemove(id,t1) {
        jQuery(id).fadeOut(t1 * 1000, function () {
          jQuery(this).hide();
        });
          handleReAppear(id);
      }


static handleReAppear(id) {
    jQuery(id).on("mouseover", function (e) {
      jQuery(this).stop(true).fadeIn(0);
    });

    jQuery(id).on("mouseleave", function (e) {
     FadeAndRemove(this);
    });
  }

它工作得很好。但是,由于元素限制,我不应该混淆 Jquery 并使用react。

我试图通过在 mouseEnter 和 mouseLeave 事件上操纵 CSS 不透明度来实现它。我面临的问题是 toast 永远不会离开使用不透明度的页面。有什么方法可以检测 toast 的不透明度何时变为 0,以便我可以在不透明度变为 0 时将其从页面中删除?

有人可以帮助我在不使用 Jquery 的情况下实现同样的目标吗?

最佳答案

对于淡入淡出的动画,我会使用 React-Spring .使用 Spring,您可以延迟开始动画,这样它会在延迟后淡出。 然后你可以添加 onMouseEnteronMouseLeave 事件处理程序来检测 toastr 的悬停。

通过此鼠标检测,您可以将 Spring 的 to 值切换为 opacity 1。这样,如果鼠标在 toast 上,它就不会淡出.

要删除 toastr,您可以使用 Spring 的 onRest 并检查 opacity 是否为零。 onRest 将在动画结束时立即调用。

状态管理在 Toastr 组件内完成,它将呈现所有显示的 toasts。该组件还将处理不透明的 toast 的移除。

对于点击事件 addToast,我使用了高阶组件 withToastr,因此我可以将 的属性添加到包含的组件。

对于事件处理,我使用 Eventemitter3 .如果您使用的是 Redux,您还可以使用它来触发 toast 。

在接下来的部分中,我将详细介绍我在以下 Codesandbox 中创建的每个组件. (注意:此处的代码片段未运行 - 要测试代码,请查看沙箱)

ToastrItem 组件

负责渲染 toast 和动画。

import React, { PureComponent } from "react";
import { Spring } from "react-spring";
import styled from "styled-components";
import PropTypes from "prop-types";

class ToastrItem extends PureComponent {
  static propTypes = {
    id: PropTypes.string,
    timeout: PropTypes.number,
    destroy: PropTypes.func
  };
  static defaultProps = {
    timeout: 5000
  };

  state = {
    hovered: false
  };

  handleRest = ({ opacity }) => {
    if (opacity === 0) {
      this.props.destroy(this.props.id);
    }
  };

  handleMouseEnter = () => {
    this.setState({
      hovered: true
    });
  };

  handleMouseLeave = () => {
    this.setState({
      hovered: false
    });
  };

  render() {
    const { message, index, timeout } = this.props;
    const { hovered } = this.state;
    return (
      <Spring
        config={{ duration: 600, delay: timeout }}
        from={{ opacity: 1.0 }}
        to={{ opacity: hovered ? 1.0 : 0 }}
        onRest={this.handleRest}
      >
        {interpolated => (
          <Wrapper>
            <ToastBox
              onMouseEnter={this.handleMouseEnter}
              onMouseLeave={this.handleMouseLeave}
              pos={index}
              opacity={interpolated.opacity}
            >
              {message}
              {/*- debug info: {JSON.stringify(interpolated)}*/}
            </ToastBox>
          </Wrapper>
        )}
      </Spring>
    );
  }
}

const Wrapper = styled.div`
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  pointer-events: none;
  z-index: 100;
`;

const ToastBox = styled.div.attrs(props => ({
  style: {
    transform: `translateY(${props.pos * 80}px)`,
    opacity: props.opacity
  }
}))`
  width: 60%;
  height: 50px;
  line-height: 50px;
  margin: 0 auto;
  color: white;
  padding: 10px;
  background: rgba(0, 0, 0, 0.8);
  text-align: center;
  box-shadow: 3px 3px 5px rgba(0, 0, 0, 0.5);
  border-radius: 10px;
  pointer-events: auto;
`;

export default ToastrItem;

Spring 正在执行前面提到的动画。鼠标事件 enter/leave 正在设置本地状态 hovered 因此我们可以更改动画结束的不透明度 - 这将避免动画。 我还尝试了 React-Springreset Prop ,但没有按预期工作。

Toastr 组件

此组件正在管理事件 toast。这里没什么特别的。它正在呈现使用 addToast 添加的 toasts 数组。 addToast 正在创建一个具有时间戳和数组索引的相对唯一的键。它是必需的,因此 React 在组件上获得了关键支持。我们也可以在这里使用 uuid 库,但我认为 timestamp-id 没问题。 如果不透明度为 0,将调用 destroy 然后它会按键过滤并更新状态。 map 就在那里,所以我们正在更新 toast 的位置。

class Toastr extends PureComponent {
  state = {
    toasts: []
  };
  addToast = (message, config) => {
    const index = this.state.toasts.length;
    const id = `toastr-${Date.now()}-${index}`;
    const ToastComponent = (
      <ToastrItem
        key={id}
        id={id}
        index={index}
        message={message}
        timeout={config.timeout || 3000}
        destroy={this.destroy}
      />
    );
    this.setState(state => ({
      toasts: [...state.toasts, ToastComponent]
    }));
  };
  destroy = id => {
    this.setState(state => ({
      toasts: [
        ...state.toasts
          .filter(toast => toast.key !== id)
          .map((toast, index) => ({
            // map for updating index
            ...toast,
            props: {
              ...toast.props,
              index: index
            }
          }))
      ]
    }));
  };

  componentDidMount() {
    emitter.on("add/toastr", this.addToast);
  }
  render() {
    const { toasts } = this.state;
    return toasts;
  }
}

export const withToastr = WrappedComponent => {
  return class extends PureComponent {
    render() {
      return <WrappedComponent addToast={actions.add} />;
    }
  };
};

在应用中的使用

我们正在使用 withToastr(App) 添加 addToast。这会将 Prop addToastr 添加到 App 组件。 然后我们渲染 Toastr 组件,它将管理和渲染我们的 toasts。 最后,我们添加一个按钮,以便我们可以触发 toast 。

class App extends Component {
  toastr;
  render() {
    const { addToast } = this.props;
    return (
      <div className="App">
        <Toastr />
        <button onClick={() => addToast("Hello", { timeout: 4000 })}>
          Show toast
        </button>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
const AppWithToasts = withToastr(App);
ReactDOM.render(<AppWithToasts />, rootElement);

结论

代码正在运行,但我会向 Spring 添加 native 属性,我还会检查转换是否更适合用例。请参阅 MessageHub example 中的示例来自 React-spring 文档。应该也可以防止淡出,但我没有检查过。

关于javascript - 在没有 Jquery 的情况下,在 mouseEnter 和 mouseLeave 上 react 停止/开始淡出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53659477/

相关文章:

javascript - 从 Chrome 扩展发送 POST 请求

html - 2 div 的问题稍微移动了

javascript - SVG 旋转不适用于 IE - Codepen 在 chrome 上完美运行

javascript - 在 React Native 中根据状态更新 tabBarLabel

javascript - 如何让 Webpack Dev Server 显示本地存储的图片?

javascript - Typescript 查找类型 - 在与 Partial 合并时正确缩小属性集

javascript - 添加到 dom 的行未选择所有复选框列

javascript - 第一次单击父子项时不会触发事件

javascript - 关闭叠加层,除非使用 jQuery/Javascript 单击 "special"Div

reactjs - 导出 Material ui主题包