Draw stage without scrollbar, also fix zoom to mouse cursor
This commit is contained in:
parent
802646f685
commit
48cf15e825
235
src/main.js
235
src/main.js
|
|
@ -61,14 +61,14 @@ let lastSaveIndex = 0;
|
||||||
|
|
||||||
let layoutElements = []
|
let layoutElements = []
|
||||||
|
|
||||||
let minFileVersion = "1.2"
|
// Version changes:
|
||||||
|
// 1.4: addShape uses frame as a reference instead of object
|
||||||
|
|
||||||
|
let minFileVersion = "1.3"
|
||||||
let maxFileVersion = "2.0"
|
let maxFileVersion = "2.0"
|
||||||
|
|
||||||
let filePath = undefined
|
let filePath = undefined
|
||||||
let fileExportPath = undefined
|
let fileExportPath = undefined
|
||||||
// let fileWidth = 1500
|
|
||||||
// let fileHeight = 1000
|
|
||||||
// let fileFps = 12
|
|
||||||
|
|
||||||
let state = "normal"
|
let state = "normal"
|
||||||
|
|
||||||
|
|
@ -261,7 +261,8 @@ let config = {
|
||||||
fileWidth: 800,
|
fileWidth: 800,
|
||||||
fileHeight: 600,
|
fileHeight: 600,
|
||||||
framerate: 24,
|
framerate: 24,
|
||||||
recentFiles: []
|
recentFiles: [],
|
||||||
|
scrollSpeed: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
function getShortcut(shortcut) {
|
function getShortcut(shortcut) {
|
||||||
|
|
@ -1132,9 +1133,11 @@ function vectorDist(a, b) {
|
||||||
|
|
||||||
function getMousePos(canvas, evt) {
|
function getMousePos(canvas, evt) {
|
||||||
var rect = canvas.getBoundingClientRect();
|
var rect = canvas.getBoundingClientRect();
|
||||||
|
const offsetX = canvas.offsetX || 0;
|
||||||
|
const offsetY = canvas.offsetY || 0;
|
||||||
return {
|
return {
|
||||||
x: (evt.clientX - rect.left) / context.zoomLevel,
|
x: (evt.clientX + offsetX - rect.left) / context.zoomLevel,
|
||||||
y: (evt.clientY - rect.top) / context.zoomLevel
|
y: (evt.clientY + offsetY - rect.top) / context.zoomLevel
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2470,12 +2473,6 @@ function _newFile(width, height, fps) {
|
||||||
saveConfig()
|
saveConfig()
|
||||||
undoStack = []
|
undoStack = []
|
||||||
redoStack = []
|
redoStack = []
|
||||||
for (let stage of document.querySelectorAll(".stage")) {
|
|
||||||
stage.width = width
|
|
||||||
stage.height = height
|
|
||||||
stage.style.width = `${width}px`
|
|
||||||
stage.style.height = `${height}px`
|
|
||||||
}
|
|
||||||
updateUI()
|
updateUI()
|
||||||
updateLayers()
|
updateLayers()
|
||||||
updateMenu()
|
updateMenu()
|
||||||
|
|
@ -2490,7 +2487,7 @@ async function newFile() {
|
||||||
async function _save(path) {
|
async function _save(path) {
|
||||||
try {
|
try {
|
||||||
const fileData = {
|
const fileData = {
|
||||||
version: "1.3",
|
version: "1.4",
|
||||||
width: config.fileWidth,
|
width: config.fileWidth,
|
||||||
height: config.fileHeight,
|
height: config.fileHeight,
|
||||||
fps: config.framerate,
|
fps: config.framerate,
|
||||||
|
|
@ -2790,24 +2787,12 @@ async function render() {
|
||||||
|
|
||||||
function updateScrollPosition(zoomFactor) {
|
function updateScrollPosition(zoomFactor) {
|
||||||
if (context.mousePos) {
|
if (context.mousePos) {
|
||||||
for (let scroller of document.querySelectorAll(".scroll")) {
|
for (let canvas of canvases) {
|
||||||
let stage = scroller.querySelector('.stage')
|
canvas.offsetX = (canvas.offsetX + context.mousePos.x) * zoomFactor - context.mousePos.x;
|
||||||
let scrollLeft = scroller.scrollLeft;
|
canvas.offsetY = (canvas.offsetY + context.mousePos.y) * zoomFactor - context.mousePos.y;
|
||||||
let scrollTop = scroller.scrollTop;
|
|
||||||
|
|
||||||
// Get the mouse position relative to the scroller (taking into account the scroller's position)
|
|
||||||
let scrollerMouseX = context.mousePos.x + scrollLeft;
|
|
||||||
let scrollerMouseY = context.mousePos.y + scrollTop;
|
|
||||||
|
|
||||||
let newScrollLeft = scrollerMouseX - (context.mousePos.x * zoomFactor);
|
|
||||||
let newScrollTop = scrollerMouseY - (context.mousePos.y * zoomFactor);
|
|
||||||
|
|
||||||
// Apply the scroll position adjustments
|
|
||||||
console.log(context.mousePos, scrollerMouseX, scrollerMouseY, newScrollLeft, newScrollTop)
|
|
||||||
scroller.scrollLeft = -newScrollLeft;
|
|
||||||
scroller.scrollTop = -newScrollTop;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function zoomIn() {
|
function zoomIn() {
|
||||||
|
|
@ -2830,77 +2815,117 @@ function zoomOut() {
|
||||||
}
|
}
|
||||||
function resetZoom() {
|
function resetZoom() {
|
||||||
context.zoomLevel = 1;
|
context.zoomLevel = 1;
|
||||||
|
for (let canvas of canvases) {
|
||||||
|
canvas.offsetX = canvas.offsetY = 0;
|
||||||
|
}
|
||||||
updateUI()
|
updateUI()
|
||||||
updateMenu()
|
updateMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
function stage() {
|
function stage() {
|
||||||
let stage = document.createElement("canvas")
|
let stage = document.createElement("canvas")
|
||||||
let scroller = document.createElement("div")
|
// let scroller = document.createElement("div")
|
||||||
let stageWrapper = document.createElement("div")
|
// let stageWrapper = document.createElement("div")
|
||||||
stage.className = "stage"
|
stage.className = "stage"
|
||||||
stage.width = config.fileWidth
|
// stage.width = config.fileWidth
|
||||||
stage.height = config.fileHeight
|
// stage.height = config.fileHeight
|
||||||
scroller.className = "scroll"
|
stage.offsetX = 0
|
||||||
stageWrapper.className = "stageWrapper"
|
stage.offsetY = 0
|
||||||
let selectionRect = document.createElement("div")
|
|
||||||
selectionRect.className = "selectionRect"
|
let lastResizeTime = 0;
|
||||||
for (let i of ["nw", "ne", "se", "sw"]) {
|
const throttleIntervalMs = 20;
|
||||||
let cornerRotateRect = document.createElement("div")
|
|
||||||
cornerRotateRect.classList.add("cornerRotateRect")
|
function updateStageCanvasSize() {
|
||||||
cornerRotateRect.classList.add(i)
|
const canvasStyles = window.getComputedStyle(stage);
|
||||||
cornerRotateRect.addEventListener('mouseup', (e) => {
|
|
||||||
const newEvent = new MouseEvent(e.type, e);
|
stage.width = parseInt(canvasStyles.width);
|
||||||
stage.dispatchEvent(newEvent)
|
stage.height = parseInt(canvasStyles.height);
|
||||||
})
|
updateUI()
|
||||||
cornerRotateRect.addEventListener('mousemove', (e) => {
|
|
||||||
const newEvent = new MouseEvent(e.type, e);
|
|
||||||
stage.dispatchEvent(newEvent)
|
|
||||||
})
|
|
||||||
selectionRect.appendChild(cornerRotateRect)
|
|
||||||
}
|
|
||||||
for (let i of ["nw", "n", "ne", "e", "se", "s", "sw", "w"]) {
|
|
||||||
let cornerRect = document.createElement("div")
|
|
||||||
cornerRect.classList.add("cornerRect")
|
|
||||||
cornerRect.classList.add(i)
|
|
||||||
cornerRect.addEventListener('mousedown', (e) => {
|
|
||||||
let bbox = undefined;
|
|
||||||
let selection = {}
|
|
||||||
for (let item of context.selection) {
|
|
||||||
if (bbox==undefined) {
|
|
||||||
bbox = structuredClone(item.bbox())
|
|
||||||
} else {
|
|
||||||
growBoundingBox(bbox, item.bbox())
|
|
||||||
}
|
|
||||||
selection[item.idx] = {x: item.x, y: item.y, scale_x: item.scale_x, scale_y: item.scale_y}
|
|
||||||
}
|
|
||||||
if (bbox != undefined) {
|
|
||||||
context.dragDirection = i
|
|
||||||
context.activeTransform = {
|
|
||||||
initial: {
|
|
||||||
x: {min: bbox.x.min, max: bbox.x.max},
|
|
||||||
y: {min: bbox.y.min, max: bbox.y.max},
|
|
||||||
selection: selection
|
|
||||||
},
|
|
||||||
current: {
|
|
||||||
x: {min: bbox.x.min, max: bbox.x.max},
|
|
||||||
y: {min: bbox.y.min, max: bbox.y.max},
|
|
||||||
selection: structuredClone(selection)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
context.activeObject.currentFrame.saveState()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
cornerRect.addEventListener('mouseup', (e) => {
|
|
||||||
const newEvent = new MouseEvent(e.type, e);
|
|
||||||
stage.dispatchEvent(newEvent)
|
|
||||||
})
|
|
||||||
cornerRect.addEventListener('mousemove', (e) => {
|
|
||||||
const newEvent = new MouseEvent(e.type, e);
|
|
||||||
stage.dispatchEvent(newEvent)
|
|
||||||
})
|
|
||||||
selectionRect.appendChild(cornerRect)
|
|
||||||
}
|
}
|
||||||
|
const resizeObserver = new ResizeObserver(() => {
|
||||||
|
const currentTime = Date.now();
|
||||||
|
|
||||||
|
if (currentTime - lastResizeTime > throttleIntervalMs) {
|
||||||
|
lastResizeTime = currentTime;
|
||||||
|
updateStageCanvasSize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resizeObserver.observe(stage);
|
||||||
|
updateStageCanvasSize()
|
||||||
|
|
||||||
|
stage.addEventListener('wheel', (event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
const deltaX = event.deltaX * config.scrollSpeed;
|
||||||
|
const deltaY = event.deltaY * config.scrollSpeed;
|
||||||
|
|
||||||
|
stage.offsetX += deltaX
|
||||||
|
stage.offsetY += deltaY
|
||||||
|
const currentTime = Date.now();
|
||||||
|
if (currentTime - lastResizeTime > throttleIntervalMs) {
|
||||||
|
lastResizeTime = currentTime;
|
||||||
|
updateUI();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// scroller.className = "scroll"
|
||||||
|
// stageWrapper.className = "stageWrapper"
|
||||||
|
// let selectionRect = document.createElement("div")
|
||||||
|
// selectionRect.className = "selectionRect"
|
||||||
|
// for (let i of ["nw", "ne", "se", "sw"]) {
|
||||||
|
// let cornerRotateRect = document.createElement("div")
|
||||||
|
// cornerRotateRect.classList.add("cornerRotateRect")
|
||||||
|
// cornerRotateRect.classList.add(i)
|
||||||
|
// cornerRotateRect.addEventListener('mouseup', (e) => {
|
||||||
|
// const newEvent = new MouseEvent(e.type, e);
|
||||||
|
// stage.dispatchEvent(newEvent)
|
||||||
|
// })
|
||||||
|
// cornerRotateRect.addEventListener('mousemove', (e) => {
|
||||||
|
// const newEvent = new MouseEvent(e.type, e);
|
||||||
|
// stage.dispatchEvent(newEvent)
|
||||||
|
// })
|
||||||
|
// selectionRect.appendChild(cornerRotateRect)
|
||||||
|
// }
|
||||||
|
// for (let i of ["nw", "n", "ne", "e", "se", "s", "sw", "w"]) {
|
||||||
|
// let cornerRect = document.createElement("div")
|
||||||
|
// cornerRect.classList.add("cornerRect")
|
||||||
|
// cornerRect.classList.add(i)
|
||||||
|
// cornerRect.addEventListener('mousedown', (e) => {
|
||||||
|
// let bbox = undefined;
|
||||||
|
// let selection = {}
|
||||||
|
// for (let item of context.selection) {
|
||||||
|
// if (bbox==undefined) {
|
||||||
|
// bbox = structuredClone(item.bbox())
|
||||||
|
// } else {
|
||||||
|
// growBoundingBox(bbox, item.bbox())
|
||||||
|
// }
|
||||||
|
// selection[item.idx] = {x: item.x, y: item.y, scale_x: item.scale_x, scale_y: item.scale_y}
|
||||||
|
// }
|
||||||
|
// if (bbox != undefined) {
|
||||||
|
// context.dragDirection = i
|
||||||
|
// context.activeTransform = {
|
||||||
|
// initial: {
|
||||||
|
// x: {min: bbox.x.min, max: bbox.x.max},
|
||||||
|
// y: {min: bbox.y.min, max: bbox.y.max},
|
||||||
|
// selection: selection
|
||||||
|
// },
|
||||||
|
// current: {
|
||||||
|
// x: {min: bbox.x.min, max: bbox.x.max},
|
||||||
|
// y: {min: bbox.y.min, max: bbox.y.max},
|
||||||
|
// selection: structuredClone(selection)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// context.activeObject.currentFrame.saveState()
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// cornerRect.addEventListener('mouseup', (e) => {
|
||||||
|
// const newEvent = new MouseEvent(e.type, e);
|
||||||
|
// stage.dispatchEvent(newEvent)
|
||||||
|
// })
|
||||||
|
// cornerRect.addEventListener('mousemove', (e) => {
|
||||||
|
// const newEvent = new MouseEvent(e.type, e);
|
||||||
|
// stage.dispatchEvent(newEvent)
|
||||||
|
// })
|
||||||
|
// selectionRect.appendChild(cornerRect)
|
||||||
|
// }
|
||||||
|
|
||||||
stage.addEventListener("drop", (e) => {
|
stage.addEventListener("drop", (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
@ -2951,9 +2976,9 @@ function stage() {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
})
|
})
|
||||||
canvases.push(stage)
|
canvases.push(stage)
|
||||||
stageWrapper.appendChild(stage)
|
// stageWrapper.appendChild(stage)
|
||||||
stageWrapper.appendChild(selectionRect)
|
// stageWrapper.appendChild(selectionRect)
|
||||||
scroller.appendChild(stageWrapper)
|
// scroller.appendChild(stageWrapper)
|
||||||
stage.addEventListener("mousedown", (e) => {
|
stage.addEventListener("mousedown", (e) => {
|
||||||
let mouse = getMousePos(stage, e)
|
let mouse = getMousePos(stage, e)
|
||||||
mouse = context.activeObject.transformMouse(mouse)
|
mouse = context.activeObject.transformMouse(mouse)
|
||||||
|
|
@ -3391,7 +3416,7 @@ function stage() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return scroller
|
return stage
|
||||||
}
|
}
|
||||||
|
|
||||||
function toolbar() {
|
function toolbar() {
|
||||||
|
|
@ -3693,11 +3718,10 @@ function timeline() {
|
||||||
});
|
});
|
||||||
resizeObserver.observe(timeline_cvs);
|
resizeObserver.observe(timeline_cvs);
|
||||||
|
|
||||||
let scrollSpeed = 1;
|
|
||||||
timeline_cvs.addEventListener('wheel', (event) => {
|
timeline_cvs.addEventListener('wheel', (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const deltaX = event.deltaX * scrollSpeed;
|
const deltaX = event.deltaX * config.scrollSpeed;
|
||||||
const deltaY = event.deltaY * scrollSpeed;
|
const deltaY = event.deltaY * config.scrollSpeed;
|
||||||
|
|
||||||
let maxScroll = context.activeObject.layers.length * layerHeight + gutterHeight - timeline_cvs.height
|
let maxScroll = context.activeObject.layers.length * layerHeight + gutterHeight - timeline_cvs.height
|
||||||
|
|
||||||
|
|
@ -4066,14 +4090,15 @@ function updateLayout(element) {
|
||||||
|
|
||||||
function updateUI() {
|
function updateUI() {
|
||||||
for (let canvas of canvases) {
|
for (let canvas of canvases) {
|
||||||
canvas.width = config.fileWidth * context.zoomLevel
|
|
||||||
canvas.height = config.fileHeight * context.zoomLevel
|
|
||||||
canvas.style.width = `${config.fileWidth * context.zoomLevel}px`
|
|
||||||
canvas.style.height = `${config.fileHeight * context.zoomLevel}px`
|
|
||||||
let ctx = canvas.getContext("2d")
|
let ctx = canvas.getContext("2d")
|
||||||
ctx.resetTransform();
|
ctx.resetTransform();
|
||||||
ctx.scale(context.zoomLevel, context.zoomLevel)
|
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
|
ctx.fillStyle = backgroundColor
|
||||||
|
ctx.fillRect(0,0,canvas.width, canvas.height)
|
||||||
|
|
||||||
|
ctx.translate(-canvas.offsetX, -canvas.offsetY)
|
||||||
|
ctx.scale(context.zoomLevel, context.zoomLevel)
|
||||||
|
|
||||||
ctx.fillStyle = "white"
|
ctx.fillStyle = "white"
|
||||||
ctx.fillRect(0,0,config.fileWidth,config.fileHeight)
|
ctx.fillRect(0,0,config.fileWidth,config.fileHeight)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue