mirror of
https://github.com/idrawjs/idraw
synced 2026-05-24 10:08:34 +00:00
feat: @idraw/board add wheelX/wheelY event
This commit is contained in:
parent
6768661292
commit
90a78c137a
4 changed files with 181 additions and 77 deletions
85
packages/board/examples/test/wheel.html
Normal file
85
packages/board/examples/test/wheel.html
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
<html>
|
||||
<head>
|
||||
<style></style>
|
||||
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
|
||||
<style>
|
||||
html,body { margin: 0; padding: 0; }
|
||||
#mount canvas {
|
||||
border-right: 1px solid #aaaaaa40;
|
||||
border-bottom: 1px solid #aaaaaa40;
|
||||
background-image:
|
||||
linear-gradient(#aaaaaa40 1px, transparent 0),
|
||||
linear-gradient(90deg, #aaaaaa40 1px, transparent 0),
|
||||
linear-gradient(#aaa 1px, transparent 0),
|
||||
linear-gradient(90deg, #aaa 1px, transparent 0);
|
||||
background-size: 10px 10px, 10px 10px, 50px 50px, 50px 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="mount"></div>
|
||||
|
||||
<script src="./../../dist/index.global.js"></script>
|
||||
<script>
|
||||
const { Board } = window.iDraw;
|
||||
const mount = document.querySelector('#mount');
|
||||
const opts = {
|
||||
width: 600,
|
||||
height: 400,
|
||||
contextWidth: 1000,
|
||||
contextHeight: 900,
|
||||
devicePixelRatio: 4,
|
||||
canScroll: true,
|
||||
}
|
||||
const board = new Board(mount, opts);
|
||||
board.on('wheelX', (deltaX) => {
|
||||
console.log('deltaX =', deltaX);
|
||||
})
|
||||
board.on('wheelY', (deltaY) => {
|
||||
console.log('deltaY =', deltaY);
|
||||
})
|
||||
|
||||
const ctx = board.getContext();
|
||||
|
||||
ctx.setFillStyle('#ffffff');
|
||||
ctx.fillRect(0, 0, opts.contextWidth, opts.contextHeight);
|
||||
|
||||
ctx.setFillStyle('#f0f0f0');
|
||||
ctx.fillRect(10, 10, 200, 120);
|
||||
|
||||
ctx.setFillStyle('#cccccc');
|
||||
ctx.fillRect(80, 80, 200, 120);
|
||||
|
||||
ctx.setFillStyle('#c0c0c0');
|
||||
ctx.fillRect(160, 160, 200, 120);
|
||||
|
||||
ctx.setFillStyle('#e0e0e0');
|
||||
ctx.fillRect(260, 260, 200, 100);
|
||||
|
||||
ctx.setFillStyle('#e0e0e0');
|
||||
ctx.fillRect(500 - 10, 500 - 10, 200, 100);
|
||||
|
||||
ctx.setFillStyle('#c0c0c0');
|
||||
ctx.fillRect(600, 600, 200, 100);
|
||||
|
||||
ctx.setFillStyle('#cccccc');
|
||||
ctx.fillRect(790, 690, 200, 100);
|
||||
|
||||
ctx.setFillStyle('#000');
|
||||
ctx.fillRect(500 - 10, 450 - 10, 20, 20);
|
||||
|
||||
// board.scale(1.5);
|
||||
// board.scrollX(-600);
|
||||
// board.scrollY(-600);
|
||||
// board.scale(1);
|
||||
|
||||
const result = board.scale(0.5);
|
||||
console.log('result =', result);
|
||||
|
||||
board.draw();
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -4,12 +4,28 @@ import { setStyle } from './util/style';
|
|||
import Context from './util/context';
|
||||
import { TypeBoardEventArgMap } from './util/event';
|
||||
|
||||
|
||||
const _canvas = Symbol('_canvas');
|
||||
const _displayCanvas = Symbol('_displayCanvas');
|
||||
const _mount = Symbol('_mount');
|
||||
const _opts = Symbol('_opts');
|
||||
const _hasRendered = Symbol('_hasRendered');
|
||||
const _ctx = Symbol('_ctx');
|
||||
const _displayCtx = Symbol('_displayCtx');
|
||||
const _originCtx = Symbol('_originCtx');
|
||||
const _watcher = Symbol('_watcher');
|
||||
const _render = Symbol('_render');
|
||||
const _calcScreen = Symbol('_calcScreen');
|
||||
const _parsePrivateOptions = Symbol('_parsePrivateOptions');
|
||||
|
||||
|
||||
type Options = {
|
||||
width: number;
|
||||
height: number;
|
||||
contextWidth: number;
|
||||
contextHeight: number;
|
||||
devicePixelRatio?: number;
|
||||
canScroll?: boolean;
|
||||
}
|
||||
|
||||
type PrivateOptions = Options & {
|
||||
|
|
@ -17,53 +33,50 @@ type PrivateOptions = Options & {
|
|||
}
|
||||
|
||||
class Board {
|
||||
private _canvas: HTMLCanvasElement;
|
||||
private _displayCanvas: HTMLCanvasElement;
|
||||
private _mount: HTMLDivElement;
|
||||
private _opts: PrivateOptions;
|
||||
private _hasRendered = false;
|
||||
private _ctx: Context;
|
||||
private _displayCtx: CanvasRenderingContext2D;
|
||||
private _originCtx: CanvasRenderingContext2D;
|
||||
// private _scaleRatio = 1;
|
||||
// private _scrollX = 0;
|
||||
// private _scrollY = 0;
|
||||
private _watcher: Watcher;
|
||||
private [_canvas]: HTMLCanvasElement;
|
||||
private [_displayCanvas]: HTMLCanvasElement;
|
||||
private [_mount]: HTMLDivElement;
|
||||
private [_opts]: PrivateOptions;
|
||||
private [_hasRendered] = false;
|
||||
private [_ctx]: Context;
|
||||
private [_displayCtx]: CanvasRenderingContext2D;
|
||||
private [_originCtx]: CanvasRenderingContext2D;
|
||||
private [_watcher]: Watcher;
|
||||
|
||||
constructor(mount: HTMLDivElement, opts: Options) {
|
||||
this._mount = mount;
|
||||
this._canvas = document.createElement('canvas');
|
||||
this._displayCanvas = document.createElement('canvas');
|
||||
this._mount.appendChild(this._displayCanvas);
|
||||
this._opts = this._parsePrivateOptions(opts);
|
||||
this._originCtx = this._canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
this._displayCtx = this._displayCanvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
this._ctx = new Context(this._originCtx, this._opts);
|
||||
this._watcher = new Watcher(this._displayCanvas);
|
||||
this._render();
|
||||
this[_mount] = mount;
|
||||
this[_canvas] = document.createElement('canvas');
|
||||
this[_displayCanvas] = document.createElement('canvas');
|
||||
this[_mount].appendChild(this[_displayCanvas]);
|
||||
this[_opts] = this[_parsePrivateOptions](opts);
|
||||
this[_originCtx] = this[_canvas].getContext('2d') as CanvasRenderingContext2D;
|
||||
this[_displayCtx] = this[_displayCanvas].getContext('2d') as CanvasRenderingContext2D;
|
||||
this[_ctx] = new Context(this[_originCtx], this[_opts]);
|
||||
this[_watcher] = new Watcher(this[_displayCanvas]);
|
||||
this[_render]();
|
||||
}
|
||||
|
||||
getDisplayContext(): CanvasRenderingContext2D {
|
||||
return this._displayCtx;
|
||||
return this[_displayCtx];
|
||||
}
|
||||
|
||||
getOriginContext(): CanvasRenderingContext2D {
|
||||
return this._displayCtx;
|
||||
return this[_displayCtx];
|
||||
}
|
||||
|
||||
getContext(): Context {
|
||||
return this._ctx;
|
||||
return this[_ctx];
|
||||
}
|
||||
|
||||
createContext(canvas: HTMLCanvasElement) {
|
||||
const opts = this._opts;
|
||||
const opts = this[_opts];
|
||||
canvas.width = opts.contextWidth * opts.devicePixelRatio;
|
||||
canvas.height = opts.contextHeight * opts.devicePixelRatio;
|
||||
return new Context(canvas.getContext('2d') as CanvasRenderingContext2D, this._opts);
|
||||
return new Context(canvas.getContext('2d') as CanvasRenderingContext2D, this[_opts]);
|
||||
}
|
||||
|
||||
createCanvas() {
|
||||
const opts = this._opts;
|
||||
const opts = this[_opts];
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth * opts.devicePixelRatio;
|
||||
canvas.height = opts.contextHeight * opts.devicePixelRatio;
|
||||
|
|
@ -72,75 +85,72 @@ class Board {
|
|||
|
||||
scale(scaleRatio: number): TypeScreenContext {
|
||||
if (scaleRatio > 0) {
|
||||
this._ctx.setTransform({ scale: scaleRatio });
|
||||
this[_ctx].setTransform({ scale: scaleRatio });
|
||||
}
|
||||
const { position, size } = this._calculateScreen();
|
||||
const { position, size } = this[_calcScreen]();
|
||||
return { position, size};
|
||||
}
|
||||
|
||||
scrollX(x: number) {
|
||||
if (x >= 0 || x < 0) {
|
||||
this._ctx.setTransform({ scrollX: x });
|
||||
this[_ctx].setTransform({ scrollX: x });
|
||||
}
|
||||
const { position, size } = this._calculateScreen();
|
||||
const { position, size } = this[_calcScreen]();
|
||||
return { position, size};
|
||||
}
|
||||
|
||||
scrollY(y: number): TypeScreenContext {
|
||||
if (y >= 0 || y < 0) {
|
||||
this._ctx.setTransform({ scrollY: y });
|
||||
this[_ctx].setTransform({ scrollY: y });
|
||||
}
|
||||
const { position, size } = this._calculateScreen();
|
||||
const { position, size } = this[_calcScreen]();
|
||||
return { position, size};
|
||||
}
|
||||
|
||||
getTransform() {
|
||||
return this._ctx.getTransform();
|
||||
return this[_ctx].getTransform();
|
||||
}
|
||||
|
||||
draw(): TypeScreenContext {
|
||||
this.clear();
|
||||
const { position, deviceSize, size } = this._calculateScreen();
|
||||
this._displayCtx.drawImage(
|
||||
this._canvas, deviceSize.x, deviceSize.y, deviceSize.w, deviceSize.h
|
||||
const { position, deviceSize, size } = this[_calcScreen]();
|
||||
this[_displayCtx].drawImage(
|
||||
this[_canvas], deviceSize.x, deviceSize.y, deviceSize.w, deviceSize.h
|
||||
);
|
||||
return { position, size};
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._displayCtx.clearRect(0, 0, this._displayCanvas.width, this._displayCanvas.height);
|
||||
this[_displayCtx].clearRect(0, 0, this[_displayCanvas].width, this[_displayCanvas].height);
|
||||
}
|
||||
|
||||
on<T extends keyof TypeBoardEventArgMap >(name: T, callback: (p: TypeBoardEventArgMap[T]) => void) {
|
||||
this._watcher.on(name, callback);
|
||||
this[_watcher].on(name, callback);
|
||||
}
|
||||
|
||||
off<T extends keyof TypeBoardEventArgMap >(name: T, callback: (p: TypeBoardEventArgMap[T]) => void) {
|
||||
this._watcher.off(name, callback);
|
||||
this[_watcher].off(name, callback);
|
||||
}
|
||||
|
||||
private _render() {
|
||||
if (this._hasRendered === true) {
|
||||
private [_render]() {
|
||||
if (this[_hasRendered] === true) {
|
||||
return;
|
||||
}
|
||||
const { width, height, contextWidth, contextHeight, devicePixelRatio } = this._opts;
|
||||
this._canvas.width = contextWidth * devicePixelRatio;
|
||||
this._canvas.height = contextHeight * devicePixelRatio;
|
||||
const { width, height, contextWidth, contextHeight, devicePixelRatio } = this[_opts];
|
||||
this[_canvas].width = contextWidth * devicePixelRatio;
|
||||
this[_canvas].height = contextHeight * devicePixelRatio;
|
||||
|
||||
this._displayCanvas.width = width * devicePixelRatio;
|
||||
this._displayCanvas.height = height * devicePixelRatio;
|
||||
this[_displayCanvas].width = width * devicePixelRatio;
|
||||
this[_displayCanvas].height = height * devicePixelRatio;
|
||||
|
||||
setStyle(this._displayCanvas, {
|
||||
setStyle(this[_displayCanvas], {
|
||||
width: `${width}px`,
|
||||
height: `${height}px`,
|
||||
});
|
||||
// this._watcher.onMove(this._onMove.bind(this));
|
||||
// this._watcher.onMoveStart(this._onMoveStart.bind(this));
|
||||
// this._watcher.onMoveEnd(this._onMoveEnd.bind(this));
|
||||
this._hasRendered = true;
|
||||
this[_hasRendered] = true;
|
||||
}
|
||||
|
||||
private _parsePrivateOptions(opts: Options): PrivateOptions {
|
||||
private [_parsePrivateOptions](opts: Options): PrivateOptions {
|
||||
const defaultOpts = {
|
||||
devicePixelRatio: 1,
|
||||
};
|
||||
|
|
@ -148,59 +158,59 @@ class Board {
|
|||
}
|
||||
|
||||
|
||||
private _calculateScreen(): {
|
||||
private [_calcScreen](): {
|
||||
size: TypeScreenSize,
|
||||
position: TypeScreenPosition,
|
||||
deviceSize: TypeScreenSize,
|
||||
} {
|
||||
const scaleRatio = this._ctx.getTransform().scale;
|
||||
const scaleRatio = this[_ctx].getTransform().scale;
|
||||
const {
|
||||
width, height, contextWidth, contextHeight,
|
||||
devicePixelRatio: pxRatio,
|
||||
} = this._opts;
|
||||
} = this[_opts];
|
||||
|
||||
// init scroll
|
||||
if (contextWidth * scaleRatio <= width) {
|
||||
// make context center
|
||||
this._ctx.setTransform({
|
||||
this[_ctx].setTransform({
|
||||
scrollX: (width - contextWidth * scaleRatio) / 2,
|
||||
})
|
||||
}
|
||||
|
||||
if (contextHeight * scaleRatio <= height) {
|
||||
// make context center
|
||||
this._ctx.setTransform({
|
||||
this[_ctx].setTransform({
|
||||
scrollY: (height - contextHeight * scaleRatio) / 2,
|
||||
})
|
||||
}
|
||||
|
||||
if (contextWidth * scaleRatio >= width && this._ctx.getTransform().scrollX > 0) {
|
||||
this._ctx.setTransform({
|
||||
if (contextWidth * scaleRatio >= width && this[_ctx].getTransform().scrollX > 0) {
|
||||
this[_ctx].setTransform({
|
||||
scrollX: 0,
|
||||
})
|
||||
}
|
||||
if (contextHeight * scaleRatio >= height && this._ctx.getTransform().scrollY > 0) {
|
||||
this._ctx.setTransform({
|
||||
if (contextHeight * scaleRatio >= height && this[_ctx].getTransform().scrollY > 0) {
|
||||
this[_ctx].setTransform({
|
||||
scrollY: 0,
|
||||
})
|
||||
}
|
||||
|
||||
const { scrollX: _scrollX, scrollY: _scrollY } = this._ctx.getTransform();
|
||||
const { scrollX: _scrollX, scrollY: _scrollY } = this[_ctx].getTransform();
|
||||
|
||||
// reset scroll
|
||||
if (_scrollX < 0 && Math.abs(_scrollX) > Math.abs(contextWidth * scaleRatio - width)) {
|
||||
this._ctx.setTransform({
|
||||
this[_ctx].setTransform({
|
||||
scrollX: 0 - Math.abs(contextWidth * scaleRatio - width)
|
||||
})
|
||||
}
|
||||
if (_scrollY < 0 && Math.abs(_scrollY) > Math.abs(contextHeight * scaleRatio - height)) {
|
||||
this._ctx.setTransform({
|
||||
this[_ctx].setTransform({
|
||||
scrollY: 0 - Math.abs(contextHeight * scaleRatio - height)
|
||||
})
|
||||
}
|
||||
|
||||
// result size
|
||||
const { scrollX, scrollY } = this._ctx.getTransform();
|
||||
const { scrollX, scrollY } = this[_ctx].getTransform();
|
||||
const size = {
|
||||
x: scrollX * scaleRatio,
|
||||
y: scrollY * scaleRatio,
|
||||
|
|
@ -224,7 +234,6 @@ class Board {
|
|||
size, position, deviceSize
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Board;
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@ export interface TypeBoardEventArgMap {
|
|||
'move': TypePoint;
|
||||
'moveStart': TypePoint;
|
||||
'moveEnd': TypePoint;
|
||||
// 'scale': number;
|
||||
// 'scrollX': number;
|
||||
// 'scrollY': number;
|
||||
'wheelX': number;
|
||||
'wheelY': number;
|
||||
}
|
||||
|
||||
export interface TypeBoardEvent {
|
||||
|
|
|
|||
|
|
@ -28,13 +28,14 @@ export class Watcher {
|
|||
|
||||
_initEvent(): void {
|
||||
const canvas = this._canvas;
|
||||
canvas.addEventListener('mousedown', this._listenMoveStart.bind(this));
|
||||
canvas.addEventListener('mousemove', this._listenMove.bind(this));
|
||||
canvas.addEventListener('mouseup', this._listenMoveEnd.bind(this));
|
||||
canvas.addEventListener('mousedown', this._listenMoveStart.bind(this), true);
|
||||
canvas.addEventListener('mousemove', this._listenMove.bind(this), true);
|
||||
canvas.addEventListener('mouseup', this._listenMoveEnd.bind(this), true);
|
||||
canvas.addEventListener('wheel', this._listenWheel.bind(this), true);
|
||||
|
||||
canvas.addEventListener('touchstart', this._listenMoveStart.bind(this));
|
||||
canvas.addEventListener('touchmove', this._listenMove.bind(this));
|
||||
canvas.addEventListener('touchend', this._listenMoveEnd.bind(this));
|
||||
canvas.addEventListener('touchstart', this._listenMoveStart.bind(this), true);
|
||||
canvas.addEventListener('touchmove', this._listenMove.bind(this), true);
|
||||
canvas.addEventListener('touchend', this._listenMoveEnd.bind(this), true);
|
||||
|
||||
// const mouseupEvent = new MouseEvent('mouseup');
|
||||
// document.querySelector('body')?.addEventListener('mousemove', (e) => {
|
||||
|
|
@ -81,6 +82,16 @@ export class Watcher {
|
|||
this._isMoving = false;
|
||||
}
|
||||
|
||||
_listenWheel(e: WheelEvent) {
|
||||
e.preventDefault();
|
||||
if (this._event.has('wheelX') && (e.deltaX > 0 || e.deltaX < 0)) {
|
||||
this._event.trigger('wheelX', e.deltaX);
|
||||
}
|
||||
if (this._event.has('wheelY') && (e.deltaY > 0 || e.deltaY < 0)) {
|
||||
this._event.trigger('wheelY', e.deltaY);
|
||||
}
|
||||
}
|
||||
|
||||
_getPosition(e: MouseEvent|TouchEvent): TypePoint {
|
||||
const canvas = this._canvas;
|
||||
let x = 0;
|
||||
|
|
|
|||
Loading…
Reference in a new issue