【问题标题】:React stop/start fade-out on mouseEnter and mouseLeave without Jquery在没有 Jquery 的情况下对 mouseEnter 和 mouseLeave 做出反应停止/开始淡出
【发布时间】:2018-12-06 20:50:53
【问题描述】:

我正在尝试将错误消息显示为 toast(React 组件),该消息将在几秒钟后淡出。但是,当用户在淡出时将鼠标悬停在吐司上时,淡出应该停止并且吐司应该恢复到其初始状态,当他将鼠标悬停在吐司上时,淡出应该重新开始.可以像这样使用 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 并做出反应。

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

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

【问题讨论】:

  • “我尝试使用 CSS 来实现这一点,方法是分别操作 toast 上 mouseEnter 和 MouseLeave 事件的不透明度和可见性。”这里有什么问题?不清楚您的问题是什么。
  • 我面临的问题是我们无法使用 opacity 为 0 从页面中删除 toast,因此即使在其不透明度变为 0 并且如果我们从使用 {display:none} 或 {visibility: hidden} 的页面,我们需要确保它仅在其不透明度变为 0 时才完成(即在淡出之后)。因为在此之前,用户可以将鼠标悬停在它上面以停止褪色效果。我能解决问题吗?

标签: javascript css reactjs


【解决方案1】:

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

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

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

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

对于点击事件addToast,我使用了更高阶的组件withToastr,所以我可以将prop添加到包含的组件中。

对于事件处理,我使用Eventemitter3。如果你使用 Redux,你也可以使用它来触发 toast。

在接下来的部分中,我将详细介绍我在以下Codesandbox 中创建的每个组件。 (注意:这里的 sn-ps 没有运行 - 测试代码请查看沙箱)

ToastrItem组件

负责渲染吐司和动画。

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-Spring 中的 reset 道具,但没有按预期工作。

Toastr组件

此组件正在管理活动的 toast。这里没什么特别的。它正在渲染使用addToast 添加的 toasts 数组。 addToast 正在创建一个具有时间戳和数组索引的相对唯一的键。它是必需的,所以 React 正在组件上获得一个关键道具。我们也可以在这里使用 uuid 库,但我认为 timestamp-id 是可以的。 destroy 将在 opacity 为 0 时被调用,然后按键过滤并更新状态。地图就在那里,所以我们正在更新祝酒词的位置。

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。这会将属性 addToastr 添加到 App 组件中。 然后我们渲染Toastr 组件,它将管理和渲染我们的toast。 最后我们添加一个按钮,以便我们可以触发 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 属性,并且我还会检查转换是否更适合用例。请参阅来自 React-spring 文档的 MessageHub example 的示例。应该也可以防止淡出但我没有检查。

【讨论】:

    【解决方案2】:

    您可能想考虑使用 Animatable 库。它使用一种很容易合并的声明式语法。

    import * from 'react-native-animatable';
    
    return(
            <Animatable.View animation="fadeOut" duration={2000} delay={1000}>
                <View>
                   {/* YOUR CONTENT */}
                </View>
            </Animatable.View>
        );
    

    https://github.com/oblador/react-native-animatable

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-29
      • 1970-01-01
      相关资源
      最近更新 更多