题目:用鼠标拖拽文字,绕着橘红色远点旋转,文字本身不旋转。
版本1.0
运行效果:
黄色点是录屏软件导致,本来是没有的。。
思路:使用canvas画线,监听鼠标事件,不断更新位置,重新绘制线。
React代码:
App.js
import './App.css';
import React from 'react'
class App extends React.Component {
constructor(props) {
super(props);
this.circle = React.createRef();
this.state = {
left: 0,
top: 0,
moveFlag: false
}
}
componentDidMount() {
document.onmousemove = (e) => {
if (this.state.moveFlag) {
let {pageX, pageY} = e;
// 1. 更改矩形位置
if (this.state.moveFlag) {
this.setState({
left: pageX - 25,
top: pageY - 10
})
}
// 2. 清空画布并绘制新的线
this._cleanCanvas((ctx) => {
this._drawNewLine(ctx, pageX, pageY);
});
}
}
}
render() {
const {left, top} = this.state
return (
<div className='app'>
<div className='circle'/>
<canvas id='canvas' width={800} height={800}/>
<div
ref={this.circle}
style={{left, top}}
className='text'>RPL10
<span
onMouseDown={(e) => this._mouseDown(e)}
onMouseUp={(e) => this._mouseUp(e)}
className='inside_circle'
/>
</div>
</div>
);
}
_mouseDown(e) {
this.setState({
moveFlag: true
})
this.circle.current.style.left = 50
}
_mouseUp(e) {
if (this.state.moveFlag) {
this.setState({
moveFlag: false
})
}
}
_cleanCanvas(drawNewLine) {
// 清空画布
let canvas = document.querySelector('#canvas');
let ctx = canvas.getContext('2d');
canvas.height = 800;
drawNewLine && drawNewLine(ctx)
}
_drawNewLine(ctx, left, top) {
ctx.moveTo(left, top);
// 4. 绘制直线
ctx.lineTo(401, 401);
// 5. 描边
ctx.stroke();
}
}
export default App;
App.css
*{
margin: 0;
padding: 0;
}
.app{
width: 800px;
height: 800px;
/*background-color: red;*/
position: relative;
border: 1px solid #000;
}
.circle{
height: 6px;
width: 6px;
border-radius: 50%;
background-color: orange;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-3px);
}
.text{
padding: 0;
margin: 0;
border: 1px solid #999999;
font-weight: bolder;
position: absolute;
display: flex;
justify-content: center;
align-items: center;
z-index: 999;
user-select: none;
}
.inside_circle{
height: 6px;
width: 6px;
border-radius: 50%;
background-color: #00ff18;
position: absolute;
cursor: pointer;
}
版本2.0
运行效果:
思路:改进了一些地方,通过两直线相交,将起点改为了与DIV的交点。同时终点距离黄色圆圈有一定距离。
方法:
- 两直线相交
- 相似
React代码:
App.js
import './App.css';
import React from 'react'
class App extends React.Component {
constructor(props) {
super(props);
this.circle = React.createRef();
this.state = {
left: 0,
top: 0,
moveFlag: false,
orangeX: 401,
orangeY: 404
}
}
componentDidMount() {
document.onmousemove = (e) => {
if (this.state.moveFlag) {
let {pageX, pageY} = e;
// 1. 更改矩形位置
if (this.state.moveFlag) {
this.setState({
left: pageX - 25,
top: pageY - 10
})
}
// 2. 清空画布并绘制新的线
this._cleanCanvas((ctx) => {
// 2.1 生成text和circle
const text = {
getBBox: () => {
return {
x: pageX,
y: pageY,
width: 50,
height: 20
}
}
}
const circle = {
center: {
x: 401,
y: 404
},
radius: 3
}
let res = this._around(text, circle)
this._drawNewLine(ctx, res);
// this._drawNewLine(ctx, pageX, pageY);
});
}
}
}
render() {
const {left, top} = this.state
return (
<div className='app'>
<div className='circle'/>
<canvas id='canvas' width={800} height={800}/>
<div
ref={this.circle}
style={{left, top}}
className='text'>RPL10
<span
onMouseDown={(e) => this._mouseDown(e)}
onMouseUp={(e) => this._mouseUp(e)}
className='inside_circle'
/>
</div>
</div>
);
}
_mouseDown(e) {
this.setState({
moveFlag: true
})
this.circle.current.style.left = 50
}
_mouseUp(e) {
if (this.state.moveFlag) {
this.setState({
moveFlag: false
})
}
}
_cleanCanvas(drawNewLine) {
// 清空画布
let canvas = document.querySelector('#canvas');
let ctx = canvas.getContext('2d');
canvas.height = 800;
drawNewLine && drawNewLine(ctx)
}
_drawNewLine(ctx, linePoint) {
ctx.moveTo(linePoint.startPt.x, linePoint.startPt.y);
// 4. 绘制直线
ctx.lineTo(linePoint.endPt.x, linePoint.endPt.y);
// 5. 描边
ctx.stroke();
}
_around(text, circle) {
const bBox = text.getBBox();
const centerBox = {
x: bBox.x,
y: bBox.y,
};
const centerCircle = {
x: circle.center.x,
y: circle.center.y
}
const radius = circle.radius;
const endPtDistCircle = 3;
let lineSeg = {
startPt: {
x: undefined,
y: undefined
},
endPt: {
x: undefined,
y: undefined
}
}
// 计算lineSeq
// 1. 获取正方形四个点,根据不同情况,判断四点交点,计算起始点
const leftTop = {
x: centerBox.x - bBox.width * 0.5,
y: centerBox.y - bBox.height * 0.5
};
const leftBottom = {
x: centerBox.x - bBox.width * 0.5,
y: centerBox.y + bBox.height * 0.5
};
const rightTop = {
x: centerBox.x + bBox.width * 0.5,
y: centerBox.y - bBox.height * 0.5
};
const rightBottom = {
x: centerBox.x + bBox.width * 0.5,
y: centerBox.y + bBox.height * 0.5
}
// 2. 计算终点
// 2.1 计算两个圆点之间的距离
let distance = this._distance(centerCircle, centerBox);
let diffX = 6 * (centerCircle.x - centerBox.x) / distance;
let diffY = 6 * (centerCircle.y - centerBox.y) / distance;
if (centerBox.x < centerCircle.x && centerBox.y < centerCircle.y) {
// 左上
lineSeg.startPt = this._interSectionPoint(leftBottom, rightBottom, centerBox, centerCircle) || this._interSectionPoint(rightTop, rightBottom, centerBox, centerCircle)
lineSeg.endPt = {
x: centerCircle.x - diffX,
y: centerCircle.y - diffY
}
} else if (centerBox.x < centerCircle.x && centerBox.y > centerCircle.y) {
// 左下
lineSeg.startPt = this._interSectionPoint(leftTop, rightTop, centerBox, centerCircle) || this._interSectionPoint(rightTop, rightBottom, centerBox, centerCircle)
lineSeg.endPt = {
x: centerCircle.x - diffX,
y: centerCircle.y - diffY
}
} else if (centerBox.x > centerCircle.x && centerBox.y < centerCircle.y) {
// 右上
lineSeg.startPt = this._interSectionPoint(leftTop, leftBottom, centerBox, centerCircle) || this._interSectionPoint(leftBottom, rightBottom, centerBox, centerCircle)
lineSeg.endPt = {
x: centerCircle.x - diffX,
y: centerCircle.y - diffY
}
} else if (centerBox.x > centerCircle.x && centerBox.y > centerCircle.y) {
// 右下
lineSeg.startPt = this._interSectionPoint(leftTop, rightTop, centerBox, centerCircle) || this._interSectionPoint(leftBottom, leftTop, centerBox, centerCircle)
lineSeg.endPt = {
x: centerCircle.x - diffX,
y: centerCircle.y - diffY
}
}
return lineSeg;
}
/**
* 直线ab和直线cd的交点坐标
*/
_interSectionPoint(a, b, c, d) {
let denominator = (b.y - a.y) * (d.x - c.x) - (a.x - b.x) * (c.y - d.y);
if (denominator === 0) {
return false;
}
let x = ((b.x - a.x) * (d.x - c.x) * (c.y - a.y)
+ (b.y - a.y) * (d.x - c.x) * a.x
- (d.y - c.y) * (b.x - a.x) * c.x) / denominator;
let y = -((b.y - a.y) * (d.y - c.y) * (c.x - a.x)
+ (b.x - a.x) * (d.y - c.y) * a.y
- (d.x - c.x) * (b.y - a.y) * c.y) / denominator;
if (
(x - a.x) * (x - b.x) <= 0 && (y - a.y) * (y - b.y) <= 0
&& (x - c.x) * (x - d.x) <= 0 && (y - c.y) * (y - d.y) <= 0
) {
// 返回交点p
return {
x: x,
y: y
}
}
//否则不相交
return false
}
/**
* 求a、b两点之间的距离
* @param a
* @param b
*/
_distance(a, b) {
let dx = Math.abs(a.x - b.x);
let dy = Math.abs(a.y - b.y);
return Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
}
}
export default App;