Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] Improve canvas zoom #44 #57

Merged
merged 6 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"d3": "^7.9.0",
"get-starknet": "^3.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
106 changes: 37 additions & 69 deletions frontend/src/canvas/Canvas.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useCallback, useRef, useEffect, useState } from 'react'
import { select, zoom, zoomIdentity } from "d3"
import useWebSocket, { ReadyState } from 'react-use-websocket'
import './Canvas.css';
// import TemplateOverlay from './TemplateOverlay.js';
Expand All @@ -7,20 +8,12 @@ import backendConfig from "../configs/backend.config.json"

const Canvas = props => {
const backendUrl = "http://" + backendConfig.host + ":" + backendConfig.port
// TODO: Pressing "Canvas" resets the view / positioning

const [canvasPositionX, setCanvasPositionX] = useState(0)
const [canvasPositionY, setCanvasPositionY] = useState(0)
const [isDragging, setIsDragging] = useState(false)
const [dragStartX, setDragStartX] = useState(0)
const [dragStartY, setDragStartY] = useState(0)

const [canvasScale, setCanvasScale] = useState(6)
const minScale = 1 // TODO: To config
const maxScale = 40
//TODO: Pressing "Canvas" resets the view / positioning
//TODO: Way to configure tick rates to give smooth xp for all users

const canvasRef = useRef(null)
const canvasPositionRef = useRef(null)
const canvasScaleRef = useRef(null)

// Read canvas config from environment variable file json
const width = canvasConfig.canvas.width
Expand All @@ -35,58 +28,33 @@ const Canvas = props => {
shouldReconnect: () => true,
},
)

// TODO: Weird positioning behavior when clicking into devtools

// Handle wheel event for zooming
const handleWheel = (e) => {
let newScale = canvasScale
if (e.deltaY < 0) {
newScale = Math.min(maxScale, newScale + 0.2)
} else {
newScale = Math.max(minScale, newScale - 0.2)
}
// TODO: Smart positioning of canvas zoom ( zoom to center of mouse pointer )
//let newCanvasPositionX = canvasPositionX
//let newCanvasPositionY = canvasPositionY
//const canvasOriginX = canvasPositionX + width / 2
//const canvasOriginY = canvasPositionY + height / 2
//setCanvasPositionX(newCanvasPositionX)
//setCanvasPositionY(newCanvasPositionY)

setCanvasScale(newScale)
}
// TODO: Weird positioning behavior when clicking into devtools
useEffect(() => {
const canvas = select(canvasPositionRef.current)
const Dzoom = zoom().on("zoom", zoomHandler)

const handlePointerDown = (e) => {
setIsDragging(true)
setDragStartX(e.clientX)
setDragStartY(e.clientY)
}
// Set default zoom level and center the canvas
canvas
.call(Dzoom)
.call(Dzoom.transform, zoomIdentity.translate(0, 0).scale(4))

const handlePointerUp = () => {
setIsDragging(false)
setDragStartX(0)
setDragStartY(0)
}
return () => {
canvas.on(".zoom", null); // Clean up zoom event listeners
};
}, []);

const handlePointerMove = (e) => {
if (isDragging) {
// TODO: Prevent dragging outside of canvas container
setCanvasPositionX(canvasPositionX + e.clientX - dragStartX)
setCanvasPositionY(canvasPositionY + e.clientY - dragStartY)
setDragStartX(e.clientX)
setDragStartY(e.clientY)
}
const zoomHandler = (event) => {
const ele = canvasScaleRef.current
const {
k: newScale,
x: newCanvasPositionX,
y: newCanvasPositionY,
} = event.transform;
const transformValue = `translate(${newCanvasPositionX}px, ${newCanvasPositionY}px) scale(${newScale})`
b-j-roberts marked this conversation as resolved.
Show resolved Hide resolved
ele.style.transform = transformValue
}

useEffect(() => {
document.addEventListener('pointerup', handlePointerUp)

return () => {
document.removeEventListener('pointerup', handlePointerUp)
}
}, [])

const [setup, setSetup] = useState(false)

const draw = useCallback((ctx, imageData) => {
Expand All @@ -104,7 +72,7 @@ const Canvas = props => {
const context = canvas.getContext('2d')

let getCanvasEndpoint = backendUrl + "/getCanvas"
fetch(getCanvasEndpoint, {mode: 'cors'}).then(response => {
fetch(getCanvasEndpoint, { mode: 'cors' }).then(response => {
return response.arrayBuffer()
}).then(data => {
let colorData = new Uint8Array(data, 0, data.byteLength)
Expand Down Expand Up @@ -205,7 +173,7 @@ const Canvas = props => {
if (props.selectedPositionX === null || props.selectedPositionY === null) {
return
}

const position = props.selectedPositionX + props.selectedPositionY * width
const colorIdx = props.selectedColorId
let placePixelEndpoint = backendUrl + "/placePixelDevnet"
Expand All @@ -228,7 +196,7 @@ const Canvas = props => {
props.setSelectedColorId(-1)
// TODO: Optimistic update
}

// TODO: Deselect pixel when clicking outside of color palette or pixel
// TODO: Show small position vec in bottom right corner of canvas
const getSelectedColor = () => {
Expand Down Expand Up @@ -287,19 +255,19 @@ const Canvas = props => {

// TODO: both place options
return (
<div className="Canvas__container" onWheel={handleWheel} onPointerDown={handlePointerDown} onPointerMove={handlePointerMove}>
<div className="Canvas__position" style={{transform: `translate(${canvasPositionX}px, ${canvasPositionY}px )`}}>
<div className="Canvas__scale" style={{transform: `scale(${canvasScale})`}}>
{ props.pixelSelectedMode && (
<div className="Canvas__selected" style={{left: props.selectedPositionX, top: props.selectedPositionY}}>
<div className="Canvas__selected__pixel" style={{backgroundColor: getSelectorsColor(), boxShadow: `0 0 .2px .1px ${getSelectorsColorInverse()} inset`}}></div>
<div className="Canvas__container">
<div ref={canvasPositionRef} className="Canvas__position">
<div ref={canvasScaleRef} className="Canvas__scale">
{props.pixelSelectedMode && (
<div className="Canvas__selected" style={{ left: props.selectedPositionX, top: props.selectedPositionY }}>
<div className="Canvas__selected__pixel" style={{ backgroundColor: getSelectorsColor(), boxShadow: `0 0 .2px .1px ${getSelectorsColorInverse()} inset` }}></div>
</div>
)}
<canvas ref={canvasRef} className="Canvas" onClick={pixelClicked}/>
<canvas ref={canvasRef} className="Canvas" onClick={pixelClicked} />
</div>
</div>
</div>
);
}

export default Canvas
export default Canvas
Loading