实现的最终效果
老规矩,先看最终要实现的效果(双击出红心)
实现步骤
实现原理
双击,顾名思义就是第一次点击和第二次点击的时间间隔小于一个固定的值
所以这个思路就是使用 new Date.getTime() 来获取时间戳,来对比前后两次的时间戳,小于某个值后就渲染一个红心效果。
那么怎么渲染这个红心效果?
这时就可以选择 ReactDOM.render 这个方法来渲染这个节点。
现在又有一个问题,就是渲染完成之后还需要将这个节点移除,React 又提供一个 ReactDOM.unmountComponentAtNode 这个方法,使用这个方法来将红心效果的节点移除。
基本概念
现在来复习这两个方法:
ReactDOM.render
这个方法相信大家应该都挺熟悉。
语法:ReactDOM.render(element, container[, callback])
在官网中的介绍是:在提供的 container 里渲染一个 React 元素,并返回对该组件的引用(或者针对无状态组件返回 null)
也就是将 React 组件渲染到指定的 container 中。
ReactDOM.unmountComponentAtNode
语法:ReactDOM.unmountComponentAtNode(container)
官网中的介绍:从 DOM 中卸载组件,会将其事件处理器(event handlers)和 state 一并清除。如果指定容器上没有对应已挂载的组件,这个函数什么也不会做。
简单来说:就是移除指定组件中的所有组件。
准备
首先介绍在实现过程中的自定义样式块(因为使用了 styled-components 这个库)
只需要知道它是做什么的就行了
Main 就不说了,就是为了好的展示而已(居中要显示的组件)
export const Main = styled.main`
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
`;
这个 Photo 就是被 Main 居中的组件,就是显示一个图片,将会在这个组件上面展示红心效果
export const Photo = styled.div`
height: 440px;
width: 300px;
background: url("https://avatars.githubusercontent.com/u/54023155?v=4")
no-repeat center center/cover;
margin: auto;
cursor: pointer;
max-width: 100%;
position: relative;
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
overflow: hidden;
`;
下面这个 HeartDiv 组件就是显示的红心效果,其中使用了 CSS 的 animation 属性来实现红心的边扩大边透明的效果,代码如下:
const grow = keyframes`
to {
transform: translate(-50%, -50%) scale(10);
opacity: 0;
}
`;
export const HeartDiv = styled.div`
position: absolute;
color: red;
/* 这里会获取渲染时鼠标点击的位置,并在这个位置显示红心效果 */
top: ${(props) => props.y}px;
left: ${(props) => props.x}px;
animation: ${grow} 0.6s linear;
transform: translate(-50%, -50%) scale(0);
`;
实现
接下来就开始实现这个红心效果
首先先把要显示的红心组件抽离出来,因为之后要渲染这个组件后又要将这个组件移除
import React from "react";
import { HeartDiv } from "./styled";
function Heart(props) {
return (
// 接收从父组件接收的鼠标点击的位置
<HeartDiv x={props.x} y={props.y}>
❤
</HeartDiv>
);
}
export default Heart;
先要看前后两次点击是否小于某个固定的值,函数实现如下:
let clickTime = 0;
const handleClick = (e) => {
if(clickTime === 0) {
// 如果是第一次点击,就先获取当前时间戳并保存
clickTime = new Date().getTime();
} else {
if((new Date().getTime() - clickTime) < 800) { // 两次点击小于某个值,这里是 800
// 具体实现
clickTime = 0; // 具体实现后,重新置为 0,再重新走一遍步骤
} else {
// 如果超过了 800,那么就需要重新获取下当前的时间戳
clickTime = new Date().getTime();
}
}
}
还挺好理解吧
但是重点是在父组件是怎么将组件渲染后,又将组件移除
接下来就是在这个函数的基础上去实现这个功能
给张图片来解释吧,这里的 <Heart /> 和 <Photo> 在准备阶段已经介绍过了,忘记的可以翻上去看看
完整代码如下(就是上面图片的代码):
import React from "react";
import ReactDOM from "react-dom";
import Heart from "./Heart";
import { Main, Photo } from "./styled";
let clickTime = 0;
function DoubleClickHeart() {
const handleClick = (e) => {
if(clickTime === 0) {
clickTime = new Date().getTime();
} else {
if((new Date().getTime() - clickTime) < 800) {
// **具体实现**
// 下面两个值就是**在组件中**双击并获取鼠标点击的位置
const xInside = e.clientX - e.target.offsetLeft;
const yInside = e.clientY - e.target.offsetTop;
// 这里就是渲染组件
// 这里的 container 对应 <Photo onClick={handleClick} id="container" />
ReactDOM.render(
<Heart x={xInside} y={yInside} />,
document.getElementById("container")
);
clickTime = 0;
// 过 1000ms 之后又将这个组件移除掉
setTimeout(() => {
ReactDOM.unmountComponentAtNode(document.getElementById("container"));
}, 1000)
} else {
clickTime = new Date().getTime();
}
}
}
return (
<Main>
<h3>Double click on the image to ❤ it</h3>
<Photo onClick={handleClick} id="container" />
</Main>
);
}
export default DoubleClickHeart;
现在就已经实现了双击出红心的效果。
最后
这篇博客更多的是介绍了 ReactDOM.render 和 ReactDOM.unmountComponentAtNode 这两个方法的配合使用,也许有更好的实现方法,可以评论介绍一下(哈哈)
这篇博客的完整源码在 这里
我将 50 Projects in 50 Days 中的例子全部用 React Hooks 实现了一遍。
github地址在这里:50-mini-projects-with-react
若有不当之处,欢迎评论指出