Skip to content

Commit

Permalink
fix: add touch device support
Browse files Browse the repository at this point in the history
  • Loading branch information
fabienwnklr committed Jan 29, 2024
1 parent 279f04d commit e9106de
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 168 deletions.
2 changes: 1 addition & 1 deletion docs/static/css/style.css

Large diffs are not rendered by default.

201 changes: 84 additions & 117 deletions docs/static/js/drawer.iife.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
window.drawer = new Drawer($el, {
defaultToolbar: false,
toolbarPosition: "innerTop",
width: 1400
width: window.innerWidth * 0.9
});
drawer.toolbar.addToolbar();
drawer.toolbar.addAllButtons();
Expand Down
30 changes: 17 additions & 13 deletions src/Drawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -573,11 +573,11 @@ class Drawer extends History {

/**
* Start drawing (mousedown)
* @param {PointerEvent} event
* @param {MouseEvent | TouchEvent} event
* @returns
*/
private _startDraw(event: PointerEvent) {
if (event.button === 2) return;
private _startDraw(event: MouseEvent | TouchEvent) {
if (event instanceof MouseEvent && event.button === 2) return;
if (this.activeTool === 'text') return;
this.#dragStartLocation = getMousePosition(this.$canvas, event);
this.ctx.beginPath();
Expand All @@ -593,11 +593,11 @@ class Drawer extends History {
}
/**
* @private _drawing
* @param {PointerEvent} event
* @param {MouseEvent | TouchEvent} event
* @returns
*/
private _drawing(event: PointerEvent) {
if (event.buttons !== 1 || this.activeTool === 'text') return; // if isDrawing is false return from here
private _drawing(event: MouseEvent | TouchEvent) {
if (event instanceof MouseEvent && event.buttons !== 1 || this.activeTool === 'text') return; // if isDrawing is false return from here

if (this.activeTool !== 'eraser') {
this.ctx.globalCompositeOperation = this.settingModal.xor ? 'xor' : 'source-over';
Expand All @@ -624,15 +624,16 @@ class Drawer extends History {
/**
* @private _drawend
* trigger when draw ended
* @param {PointerEvent} event
* @param {MouseEvent | TouchEvent} event
*/
private _drawend(event: PointerEvent) {
if (event.pointerType !== 'mouse' || event.button === 0) {
private _drawend(event: MouseEvent | TouchEvent) {
if ((event instanceof MouseEvent && event.button !== 2) || event instanceof TouchEvent) {
if (this.isShape()) {
this._restoreSnapshot();
}

const position =
this.activeTool === 'text' ? { x: event.clientX, y: event.clientY } : getMousePosition(this.$canvas, event);
this.activeTool === 'text' ? getMousePosition(this.$canvas, event, false) : getMousePosition(this.$canvas, event);

this.toolbar._manageUndoRedoBtn();
this._draw(position);
Expand Down Expand Up @@ -980,9 +981,12 @@ class Drawer extends History {
this._drawing = throttle(this._drawing, 10);
this._drawend = throttle(this._drawend, 10);

this.$canvas.addEventListener('pointerdown', this._startDraw.bind(this), false);
this.$canvas.addEventListener('pointermove', this._drawing.bind(this), false);
this.$canvas.addEventListener('pointerup', this._drawend.bind(this), false);
this.$canvas.addEventListener('mousedown', this._startDraw.bind(this), false);
this.$canvas.addEventListener('touchstart', this._startDraw.bind(this), false);
this.$canvas.addEventListener('mousemove', this._drawing.bind(this), false);
this.$canvas.addEventListener('touchmove', this._drawing.bind(this), false);
this.$canvas.addEventListener('mouseup', this._drawend.bind(this), false);
this.$canvas.addEventListener('touchend', this._drawend.bind(this), false);

this.$canvas.addEventListener('drawer.update.color', this._updateCursor.bind(this));
this.$canvas.addEventListener('drawer.update.lineThickness', this._updateCursor.bind(this));
Expand Down
7 changes: 4 additions & 3 deletions src/css/drawer.css
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ html[data-theme='dark'] {
position: relative;
z-index: 100;
font-family: helvetica, 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
touch-action: none;
}

.drawer-container.expanded {
Expand Down Expand Up @@ -243,9 +244,9 @@ html[data-theme='dark'] {
background-color: transparent;
}

.drawer-container .btn.btn-primary:not(:disabled):not(.active):hover,
.drawer-menu .btn.btn-primary:not(:disabled):not(.active):hover,
.drawer-modal .btn.btn-primary:not(:disabled):not(.active):hover {
.drawer-container .btn.btn-drawer-primary:not(:disabled):not(.active):hover,
.drawer-menu .btn.btn-drawer-primary:not(:disabled):not(.active):hover,
.drawer-modal .btn.btn-drawer-primary:not(:disabled):not(.active):hover {
background-color: var(--drawer-primary-bg-hover);
}

Expand Down
55 changes: 29 additions & 26 deletions src/ui/Toolbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export class Toolbar {
return new Promise((resolve, reject) => {
try {
if (this.$toolbar && !this.$undoBtn) {
const undoBtn = /*html*/ `<button title="${'Redo'}" class="btn btn-primary" disabled>${UndoIcon}</button>`;
const undoBtn = /*html*/ `<button title="${'Redo'}" class="btn btn-drawer-primary" disabled>${UndoIcon}</button>`;
const $undoBtn = stringToHTMLElement<HTMLButtonElement>(undoBtn);
this.$undoBtn = $undoBtn;

Expand Down Expand Up @@ -184,7 +184,7 @@ export class Toolbar {
return new Promise((resolve, reject) => {
try {
if (this.$toolbar && !this.$redoBtn) {
const redoBtn = /*html*/ `<button title="${'Redo'}" class="btn btn-primary" disabled>${RedoIcon}</button>`;
const redoBtn = /*html*/ `<button title="${'Redo'}" class="btn btn-drawer-primary" disabled>${RedoIcon}</button>`;
const $redoBtn = stringToHTMLElement<HTMLButtonElement>(redoBtn);
this.$redoBtn = $redoBtn;

Expand Down Expand Up @@ -223,7 +223,7 @@ export class Toolbar {
return new Promise((resolve, reject) => {
try {
if (this.$toolbar && !this.$brushBtn) {
const brushBtn = /*html*/ `<button title="${'Brush'}" class="btn btn-primary active">${BrushIcon}</button>`;
const brushBtn = /*html*/ `<button title="${'Brush'}" class="btn btn-drawer-primary active">${BrushIcon}</button>`;
const $brushBtn = stringToHTMLElement<HTMLButtonElement>(brushBtn);
this.$brushBtn = $brushBtn;

Expand Down Expand Up @@ -259,7 +259,7 @@ export class Toolbar {
return new Promise((resolve, reject) => {
try {
if (this.$toolbar && !this.$eraserBtn) {
const eraserBtn = /*html*/ `<button title="${'Eraser'}" class="btn btn-primary">${EraserIcon}</button>`;
const eraserBtn = /*html*/ `<button title="${'Eraser'}" class="btn btn-drawer-primary">${EraserIcon}</button>`;
const $eraserBtn = stringToHTMLElement<HTMLButtonElement>(eraserBtn);
this.$eraserBtn = $eraserBtn;

Expand Down Expand Up @@ -295,7 +295,7 @@ export class Toolbar {
return new Promise((resolve, reject) => {
try {
if (this.$toolbar && !this.$textBtn) {
const textBtn = /*html*/ `<button title="${'Text zone'}" class="btn btn-primary">${TextIcon}</button>`;
const textBtn = /*html*/ `<button title="${'Text zone'}" class="btn btn-drawer-primary">${TextIcon}</button>`;
const $textBtn = stringToHTMLElement<HTMLButtonElement>(textBtn);
this.$textBtn = $textBtn;

Expand Down Expand Up @@ -348,13 +348,13 @@ export class Toolbar {
const drawGroupMenu = /*html*/ `
<ul class="drawer-menu">
<li class="drawer-menu-item">
<button data-tool="brush" title=${'Brush'} class="btn btn-primary">${BrushIcon}</button>
<button data-tool="brush" title=${'Brush'} class="btn btn-drawer-primary">${BrushIcon}</button>
</li>
<li class="drawer-menu-item">
<button data-tool="eraser" title=${'Eraser'} class="btn btn-primary">${EraserIcon}</button>
<button data-tool="eraser" title=${'Eraser'} class="btn btn-drawer-primary">${EraserIcon}</button>
</li>
<li class="drawer-menu-item">
<button data-tool="text" title=${'Text zone'} class="btn btn-primary">${TextIcon}</button>
<button data-tool="text" title=${'Text zone'} class="btn btn-drawer-primary">${TextIcon}</button>
</li>
</ul>`;

Expand Down Expand Up @@ -410,7 +410,7 @@ export class Toolbar {
return new Promise((resolve, reject) => {
try {
if (this.$toolbar && !this.$clearBtn) {
const clearBtn = /*html*/ `<button title="${'Clear draw'}" class="btn btn-primary">${ClearIcon}</button>`;
const clearBtn = /*html*/ `<button title="${'Clear draw'}" class="btn btn-drawer-primary">${ClearIcon}</button>`;
const $clearBtn = stringToHTMLElement<HTMLButtonElement>(clearBtn);
this.$clearBtn = $clearBtn;

Expand All @@ -423,6 +423,9 @@ export class Toolbar {
if (!this.drawer.clearModal) {
this.drawer.clearModal = new ConfirmModal(this.drawer, {
message: 'Do you want to delete the entire drawing?',
onConfirm: () => {
this.drawer.destroy();
}
});
}
this.drawer.clearModal.show();
Expand Down Expand Up @@ -451,30 +454,30 @@ export class Toolbar {
return new Promise((resolve, reject) => {
try {
if (this.$toolbar && !this.$shapeBtn) {
const shapeBtn = /*html*/ `<button title="${'Draw shape'}" class="btn btn-primary btn-shape">${ShapeIcon}</button>`;
const shapeBtn = /*html*/ `<button title="${'Draw shape'}" class="btn btn-drawer-primary btn-shape">${ShapeIcon}</button>`;

const shapeMenu = /*html*/ `
<ul class="drawer-menu">
<li class="drawer-menu-item">
<button data-shape="triangle" class="btn btn-primary triangle" title="${'Triangle'}">${TriangleIcon}</button>
<button data-shape="triangle" class="btn btn-drawer-primary triangle" title="${'Triangle'}">${TriangleIcon}</button>
</li>
<li class="drawer-menu-item">
<button data-shape="rect" class="btn btn-primary rect" title="${'Rectangle'}">${RectIcon}</button>
<button data-shape="rect" class="btn btn-drawer-primary rect" title="${'Rectangle'}">${RectIcon}</button>
</li>
<li class="drawer-menu-item">
<button data-shape="square" class="btn btn-primary square" title="${'Square'}">${SquareIcon}</button>
<button data-shape="square" class="btn btn-drawer-primary square" title="${'Square'}">${SquareIcon}</button>
</li>
<li class="drawer-menu-item">
<button data-shape="line" class="btn btn-primary line" title="${'Line'}">${LineIcon}</button>
<button data-shape="line" class="btn btn-drawer-primary line" title="${'Line'}">${LineIcon}</button>
</li>
<li class="drawer-menu-item">
<button data-shape="arrow" class="btn btn-primary arrow" title="${'Arrow'}">${ArrowIcon}</button>
<button data-shape="arrow" class="btn btn-drawer-primary arrow" title="${'Arrow'}">${ArrowIcon}</button>
</li>
<li class="drawer-menu-item">
<button data-shape="circle" class="btn btn-primary circle" title="${'Circle'}">${CircleIcon}</button>
<button data-shape="circle" class="btn btn-drawer-primary circle" title="${'Circle'}">${CircleIcon}</button>
</li>
<li class="drawer-menu-item">
<button data-shape="ellipse" class="btn btn-primary circle" title="${'Ellipse'}">${EllipseIcon}</button>
<button data-shape="ellipse" class="btn btn-drawer-primary circle" title="${'Ellipse'}">${EllipseIcon}</button>
</li>
</ul>`;

Expand Down Expand Up @@ -577,7 +580,7 @@ export class Toolbar {
if (this.$toolbar && !this.$colorPicker) {
const colorPickerContainer = /*html*/ `
<div class="container-colorpicker">
<input class="btn btn-primary" id="colopicker-${
<input class="btn btn-drawer-primary" id="colopicker-${
this.drawer.options.id
}" class="" type="text" title="${'Color'}" value="${this.drawer.options.color}" data-coloris/>
</div>
Expand Down Expand Up @@ -636,7 +639,7 @@ export class Toolbar {
const uploadFile = /*html*/ `
<div class="container-uploadFile">
<input tabindex="-1" id="${this.drawer.options.id}-uploadfile" title="${'Color'}" class="" type="file" />
<label tabindex="0" title="${'Upload file'}" accept="image/png, image/jpeg, .svg" class="btn btn-primary" for="${
<label tabindex="0" title="${'Upload file'}" accept="image/png, image/jpeg, .svg" class="btn btn-drawer-primary" for="${
this.drawer.options.id
}-uploadfile">
${UploadIcon}
Expand Down Expand Up @@ -679,7 +682,7 @@ export class Toolbar {
addDownloadBtn(action?: action<HTMLButtonElement>): Promise<HTMLButtonElement> {
return new Promise((resolve, reject) => {
if (this.$toolbar && !this.$downloadBtn) {
const download = /*html*/ `<button title="${'Download'}" class="btn btn-primary">${DownloadIcon}</button>`;
const download = /*html*/ `<button title="${'Download'}" class="btn btn-drawer-primary">${DownloadIcon}</button>`;
const $downloadBtn = stringToHTMLElement<HTMLButtonElement>(download);
this.$downloadBtn = $downloadBtn;

Expand Down Expand Up @@ -767,7 +770,7 @@ export class Toolbar {
addPickColorButton(action?: action<HTMLButtonElement>): Promise<HTMLButtonElement> {
return new Promise((resolve, reject) => {
if (this.$toolbar && !this.$pickColorBtn) {
const pickColor = /*html*/ `<button title="${'Pick color'}" class="btn btn-primary">${ExpandIcon}</button>`;
const pickColor = /*html*/ `<button title="${'Pick color'}" class="btn btn-drawer-primary">${ExpandIcon}</button>`;
const $pickColorBtn = stringToHTMLElement<HTMLButtonElement>(pickColor);
this.$pickColorBtn = $pickColorBtn;

Expand Down Expand Up @@ -824,7 +827,7 @@ export class Toolbar {
addExpandButton(action?: action<HTMLButtonElement>): Promise<HTMLButtonElement> {
return new Promise((resolve, reject) => {
if (this.$toolbar && !this.$expandBtn) {
const expand = /*html*/ `<button title="${'Expand'}" class="btn btn-primary">${ExpandIcon}</button>`;
const expand = /*html*/ `<button title="${'Expand'}" class="btn btn-drawer-primary">${ExpandIcon}</button>`;
const $expandBtn = stringToHTMLElement<HTMLButtonElement>(expand);
this.$expandBtn = $expandBtn;

Expand Down Expand Up @@ -856,7 +859,7 @@ export class Toolbar {
addFullscreenButton(action?: action<HTMLButtonElement>): Promise<HTMLButtonElement> {
return new Promise((resolve, reject) => {
if (this.$toolbar && !this.$fullscreenBtn) {
const fullscreen = /*html*/ `<button title="${'Fullscreen'}" class="btn btn-primary">${FullscreenIcon}</button>`;
const fullscreen = /*html*/ `<button title="${'Fullscreen'}" class="btn btn-drawer-primary">${FullscreenIcon}</button>`;
const $fullscreenBtn = stringToHTMLElement<HTMLButtonElement>(fullscreen);
this.$fullscreenBtn = $fullscreenBtn;

Expand All @@ -881,7 +884,7 @@ export class Toolbar {

async addCloseButton(action?: action<HTMLButtonElement>): Promise<HTMLButtonElement | undefined> {
if (this.$toolbar && !this.$closeBtn) {
const close = /*html*/ `<button title="${'Close'}" class="btn btn-primary">${CloseIcon}</button>`;
const close = /*html*/ `<button title="${'Close'}" class="btn btn-drawer-primary">${CloseIcon}</button>`;
const $closeBtn = stringToHTMLElement<HTMLButtonElement>(close);
this.$closeBtn = $closeBtn;

Expand Down Expand Up @@ -913,7 +916,7 @@ export class Toolbar {
addSettingBtn(action?: action<HTMLButtonElement>): Promise<HTMLButtonElement> {
return new Promise((resolve, reject) => {
if (this.$toolbar && !this.$settingBtn) {
const settingBtn = /*html*/ `<button title="${'Settings'}" class="btn btn-primary">${SettingIcon}</button>`;
const settingBtn = /*html*/ `<button title="${'Settings'}" class="btn btn-drawer-primary">${SettingIcon}</button>`;
const $settingBtn = stringToHTMLElement<HTMLButtonElement>(settingBtn);
this.$settingBtn = $settingBtn;

Expand Down Expand Up @@ -955,7 +958,7 @@ export class Toolbar {
): Promise<HTMLButtonElement> {
return new Promise((resolve, reject) => {
if (this.$toolbar && !this.customBtn[name]) {
const customBtn = /*html*/ `<button title="${title}" class="btn btn-primary">${label}</button>`;
const customBtn = /*html*/ `<button title="${title}" class="btn btn-drawer-primary">${label}</button>`;
const $customBtn = stringToHTMLElement<HTMLButtonElement>(customBtn);
this.customBtn[name] = $customBtn;

Expand Down
27 changes: 22 additions & 5 deletions src/utils/infos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,30 @@ export function isTactil(): boolean {
/**
* returns the xy point where the mouse event was occured inside an element.
* @param {HTMLElement} $canvas
* @param {PointerEvent} evt
* @param {MouseEvent | TouchEvent} evt
*/
export function getMousePosition($canvas: HTMLElement | SVGElement, evt: PointerEvent): { x: number; y: number } {
export function getMousePosition($canvas: HTMLElement | SVGElement, evt: MouseEvent | TouchEvent, relative = true): { x: number; y: number } {
let posTarget;
if (evt instanceof TouchEvent) {
posTarget = evt.touches[0];
if (evt.type === "touchend") {
posTarget = evt.changedTouches[0];
}
} else {
posTarget = evt;
}
const rect = $canvas.getBoundingClientRect();
const x = posTarget.clientX;
const y = posTarget.clientY;

if (relative) {
return {
x: x - rect.left,
y: y - rect.top,
};
}

return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top,
};
x, y
}
}
2 changes: 1 addition & 1 deletion src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type IIsObject = (item: any) => boolean;
* @return {Boolean} Whether or not @item is an object
*/
export const isObject: IIsObject = (item: any): boolean => {
return item === Object(item) && !Array.isArray(item);
return item === Object(item) && !Array.isArray(item) && typeof item !== 'function';
};

export function isTruthy(t: any): boolean {
Expand Down
2 changes: 1 addition & 1 deletion vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default defineConfig({
closeBundle: async () => {
if (process.env.NODE_ENV !== 'test') {
console.log('Build docs...');
execSync('npm run build:docs'); // run during closeBundle hook. https://rollupjs.org/guide/en/#closebundle
execSync('npm run doc:js'); // run during closeBundle hook. https://rollupjs.org/guide/en/#closebundle
console.log('Docs build !');
}
},
Expand Down

0 comments on commit e9106de

Please sign in to comment.