項目地址(歡迎star):react-draggable-layercss
技術博客:www.linxiangjun.com前端
2019年以來,愈來愈多的公司與項目都選擇使用強類型語言TypeScript做爲主要語言,能夠預見的是2020年TypeScript將成爲前端開發所須要具有的能力之一。react
目前前端工程師使用最多的無疑問是微軟開源的VSCode,它自己提供的功能並很少,可是經過支持插件的方式使得其具有了IDE的強大能力。同時,VSCode是使用TypeScript基於Eletron開發的,使得它對於TypeScript的支持很是的強大。若是你使用的編輯器是VSCode,那麼項目中徹底使用TypeScript編程將會大大的提升編程效率,甚至使用一段時間後你會發現,已經漸漸離不開TypeScript。git
應用新的語言仍是語法都會面臨着許多困難和陷阱,這個時候若是可以參考一些現成的寫法確定會帶來收益。這也是我寫這篇文章的初衷。github
下面是react-draggable-layer使用到的一些技術細節:npm
實現一個彈層類組件,React提供了Portals
方法,可以將子節點渲染到存在於父組件之外的DOM節點上。咱們先實現一個封裝了Portals
方法的通用組件:編程
components/Portal.jsx前端工程師
import React, { useEffect, useRef } from "react";
import ReactDOM from "react-dom";
interface PortalProps {
children: React.ReactElement;
}
const Portal: React.FC<PortalProps> = props => {
// 使用useRef建立ref對象,能夠用來保存節點
const { current: el } = useRef(document.createElement("div"));
// 等同於類組件的componentDidMount方法
useEffect(() => {
const root = document.getElementById("root");
if (root) {
root.appendChild(el);
}
// 組件銷燬後記得移除div
return () => {
if (root) {
root.removeChild(el);
}
};
}, []);
return ReactDOM.createPortal(props.children, el);
};
export default Portal;
複製代碼
有了Portal組件以後在可拖拽彈層組件中引入,這樣組件在使用時就能夠掛載到root
節點下。在組件中樣主要是以css-in-js的方式來實現的,它的優勢有:app
這並非說它就沒有缺點的,一樣的,使用css-in-js來開發會使得不少高階css用法很是的複雜,因此須要根據項目狀況來辯證的思考與選擇。不過對於此項目來講css-in-js就很是的合適,它使得咱們能夠將更多的精力放在組件化上面。另外這裏推薦下emotion這個庫。dom
這裏先來基於項目說一下TypeScript開發React函數式組件的規範:
interface DraggableLayerProps {
children?: React.ReactElement;
titleName?: string;
visible?: boolean;
onClose?: () => void;
}
const DraggableLayer: React.FC<DraggableLayerProps> = props => <Component />
export default DraggableLayer
複製代碼
使用函數式組件時須要將組件申明爲React.FC
類型,也就是Functional Component的意思,另外props
須要申明各個參數的類型,而後經過泛型傳遞給React.FC
。
可拖拽功能的實現是基於實時計算組件x軸與y軸的距離來實現的,具體代碼比較簡潔:
let initX = 0;
let initY = 0;
let isMouseDown = false;
const handleMouseDown = (e: MouseEvent) => {
isMouseDown = true;
initX = e.offsetX;
initY = e.offsetY;
draggableRef.current.style.opacity = 0.5;
};
const handleMouseMove = (e: MouseEvent) => {
if (isMouseDown) {
const x = e.clientX - initX;
const y = e.clientY - initY;
draggableRef.current.style.left = `${x}px`;
draggableRef.current.style.top = `${y}px`;
}
};
const handleMouseUp = () => {
isMouseDown = false;
if (draggableRef && draggableRef.current) {
draggableRef.current.style.opacity = 1;
}
};
複製代碼
在組件獲取props
時有不少可選的參數,好比titleName
,由於使用了TypeScript,因此對可選參數進行賦值操做時提示錯誤信息,這點在開發時可以有效的避免出錯。另外,組件銷燬後須要在useEffect
的return
回調函數中清除綁定的event。這些細節須要注意。
很是歡迎嘗試本項目,能夠直接使用npm install react-draggable-layer
來使用,有好的建議也但願可以收到提交的issues。一些更加詳細的使用說明在項目中的README文件中。若是本文或者項目對你有幫助的話,很是高興您可以點下star支持。