feat: @idraw/board add wheelX/wheelY event

This commit is contained in:
chenshenhai 2021-06-12 11:32:28 +08:00
parent 6768661292
commit 90a78c137a
4 changed files with 181 additions and 77 deletions

View 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>

View file

@ -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;

View file

@ -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 {

View file

@ -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;