Skip to content

Instantly share code, notes, and snippets.

@keelii
Last active October 9, 2023 06:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save keelii/d0f278ff571f0addc22f5e2a1ba80abc to your computer and use it in GitHub Desktop.
Save keelii/d0f278ff571f0addc22f5e2a1ba80abc to your computer and use it in GitHub Desktop.
BezierCurve.jsx
const VIEW_SIZE = 100
function getCursorPosition(canvas, event) {
const rect = canvas.getBoundingClientRect()
const x = event.pageX - rect.left
const y = event.pageY - rect.top
return { x, y }
}
class EasingCanvas extends React.Component {
get canvas() {
return this.canvasRef.current
}
get ctx() {
return this.canvas.getContext("2d")
}
get offset() {
return {
x: (this.props.width - VIEW_SIZE) / 2,
y: this.props.height - (this.props.height - VIEW_SIZE) / 2,
}
}
constructor(props) {
super(props)
this.canvasRef = React.createRef()
this.ctrl = null
this.prevX = 0
this.prevY = 0
this.value = [...this.props.value]
}
componentDidMount() {
this.draw()
}
onMouseDown = (e) => {
this.mousedown = true
this.prevX = e.clientX
this.prevY = e.clientY
const point = getCursorPosition(this.canvasRef.current, e)
const image = this.ctx.getImageData(point.x, point.y, 1, 1)
if (image.data[2] === 255) {
this.ctrl = "cp1"
}
if (image.data[2] === 254) {
this.ctrl = "cp2"
}
}
onMouseMove = (e) => {
if (!this.mousedown) return
const deltaX = e.clientX - this.prevX
const deltaY = e.clientY - this.prevY
this.prevX = e.clientX
this.prevY = e.clientY
const [cp1x, cp1y, cp2x, cp2y, x, y] = this.value
const value = [cp1x, cp1y, cp2x, cp2y, x, y]
if (this.ctrl === "cp1") {
this.value = [cp1x + deltaX, cp1y + deltaY, cp2x, cp2y, x, y]
}
if (this.ctrl === "cp2") {
this.value = [cp1x, cp1y, cp2x + deltaX, cp2y + deltaY, x, y]
}
this.forceUpdate()
this.draw()
}
onMouseUp = (e) => {
this.mousedown = false
this.prevX = 0
this.prevY = 0
this.ctrl = null
}
draw() {
this.clearCanvas()
this.drawAxis(this.ctx)
this.drawCurve(this.ctx)
this.drawControl(this.ctx)
}
clearCanvas() {
this.ctx.clearRect(0, 0, this.props.width, this.props.height)
}
getBezierCurvePoints() {
const offsetX = this.offset.x
const offsetY = this.offset.y
const x = offsetX + VIEW_SIZE
const y = offsetY - VIEW_SIZE
return [
offsetX + 35,
offsetY + 15,
x - 35,
y - 15,
x,
y,
]
}
drawControl(ctx) {
const [
cp1x,
cp1y,
cp2x,
cp2y,
x,
y,
] = this.value
ctx.save()
ctx.beginPath()
ctx.setLineDash([2, 4])
ctx.moveTo(this.offset.x, this.offset.y)
ctx.lineTo(cp1x, cp1y)
ctx.moveTo(x, y)
ctx.lineTo(cp2x, cp2y)
ctx.stroke()
ctx.fillStyle = "rgb(0, 0, 255)"
ctx.fillRect(cp1x - 5, cp1y - 5, 10, 10)
ctx.fillStyle = "rgb(0, 0, 254)"
ctx.fillRect(cp2x - 5, cp2y - 5, 10, 10)
ctx.restore()
}
drawCurve(ctx) {
const [
cp1x,
cp1y,
cp2x,
cp2y,
x,
y,
] = this.value
ctx.save()
ctx.beginPath()
ctx.moveTo(this.offset.x, this.offset.y)
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
ctx.stroke()
ctx.restore()
}
drawAxis(ctx) {
const offsetX = this.offset.x
const offsetY = this.offset.y
ctx.save()
ctx.strokeStyle = "black"
ctx.beginPath()
ctx.moveTo(offsetX, this.props.height)
ctx.lineTo(offsetX, 0)
ctx.moveTo(0, offsetY)
ctx.lineTo(this.props.width, offsetY)
ctx.stroke()
const ox = offsetX
const oy = offsetY
const [
cp1x,
cp1y,
cp2x,
cp2y,
x,
y,
] = this.value
ctx.fillStyle = "rgba(0, 0, 0, 0.1)"
ctx.fillRect(ox, ox, oy - ox, oy - ox)
ctx.fillStyle = "rgba(255, 0, 0, 0.5)"
ctx.fillRect(ox - 5, oy - 5, 10, 10)
ctx.fillRect(x - 5, y - 5, 10, 10)
ctx.restore()
}
render() {
const [cp1x, cp1y, cp2x, cp2y] = this.value
return (
<div>
[ {(cp1x - this.offset.x) / VIEW_SIZE}, {-(cp1y - this.offset.y) / VIEW_SIZE},{" "}
{(cp2x - this.offset.x) / VIEW_SIZE}, {-(cp2y - this.offset.y) / VIEW_SIZE} ]<br />
<canvas
onMouseDown={this.onMouseDown}
onMouseMove={this.onMouseMove}
onMouseUp={this.onMouseUp}
ref={this.canvasRef}
width={this.props.width}
height={this.props.height}
style={{ background: "#fff" }}
/>
</div>
)
}
}
function BezierEasingCanvas() {
return (
<div style={{ background: "#eee", padding: 100 }}>
<div>
<strong>贝塞尔缓动函数生成器</strong>
<EasingCanvas value={[75, 155, 105, 25, 140, 40]} width={180} height={180} />
</div>
</div>
)
}
function App() {
return <BezierEasingCanvas />
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment