/**
* @class
* @description Une librairie qui permet de créer un canvas sur lequel dessiner avec la souris ou avec le doigt (sur mobile). Plusieurs instances peuvent cohabiter sur la même page.
* @author TIMMatane
* @version 1.0.0 (Janvier 2020)
*/
class SimpleCanvasJS {
/**
* @description Une librairie qui permet de créer un canvas sur lequel dessiner avec la souris ou avec le doigt (sur mobile). Plusieurs instances peuvent cohabiter sur la même page.
* @param {number} width La largeur du canvas en pixels
* @param {number} height La hauteur du canvas en pixels
* @param {HTMLElement} container Le conteneur dans lequel ajouter le canvas
* @param {string | null} [fillColor = null] La couleur de remplissage. La valeur null défini le fond comme transparent
* @param {string} [brushColor = #000000] La couleur de la brosse
* @param {number}[brushSize = 1] La dimension de la brosse
* @returns {SimpleCanvasJS} La référence au SimpleCanvasJS créé
* @example <caption>1) Créer un canvas de 250X250 avec remplissage en blanc, trait en rouge et d'une grosseur de 5 pixels et la placer dans un conteneur</caption>
*let dessin = new SimpleCanvasJS(250, 250, document.querySelector("#canvasDiv"), "#ffffff", "#ff0000", 5);
*dessin.brush("#00ff00", 5); //changer la brosse par défaut
*dessin.fill("#ff0000"); //remplir le canvas d'une couleur
*/
constructor(width, height, container = document.body, fillColor = null, brushColor = "#000000", brushSize = 1) {
//dimensions
this.width = width;
this.height = height;
/**
* @member SimpleCanvasJS#canvas
* @type {HTMLCanvasElement}
* @description Référence a l'élément canvas de SimpleCanvasJS
*/
this.canvas = document.createElement("canvas");
this.canvas.setAttribute("width", this.width);
this.canvas.setAttribute("height", this.height);
//ajout de la classe de base d'un SimpleCanvasJS
this.canvas.classList.add("SimpleCanvasJS");
/**
* @member SimpleCanvasJS#context
* @type {CanvasRenderingContext2D}
* @description Référence au contexte 2D du canvas
*/
this.context = this.canvas.getContext("2d");
/**
* @member SimpleCanvasJS#fillColor
* @type {string | null}
* @description Couleur hexadecimale de remplissage
*/
this.fillColor = fillColor;
if (this.fillColor) {
this.fill(this.fillColor);
}
this.brushColor = brushColor;
this.brushSize = brushSize;
//apliquer la brosse par défaut
this.brush(this.brushColor, this.brushSize);
this.#setupEvents();
this.lastXPos = 0;
this.lastYpos = 0;
this.drawing = false;
//ajout du canvas dans son conteneur
container.appendChild(this.canvas);
}
/**
* @description Change la brosse utilisée
* @param {string} brushColor La couleur de la brosse
* @param {number} brushSize La dimension de la brosse
*/
brush(brushColor, brushSize) {
this.brushColor = brushColor;
this.brushSize = brushSize;
this.context.lineWidth = brushSize;
this.context.strokeStyle = brushColor;
}
/**
* @description Remplir l'arrière pland du canvas d'une couleur
* @param {string} [fillColor = this.fillColor] La couleur voulue
*/
fill(fillColor = this.fillColor) {
this.fillColor = fillColor;
this.context.fillStyle = fillColor;
this.context.fillRect(0, 0, this.width, this.height);
}
/**
* @description Vider la canvas de son contenu
* @param {boolean} [refill = true] Est-ce qu'une couleur de fond doit être utilisée après avoir vidé le canvas?
*/
clear(refill = true) {
this.context.clearRect(0, 0, this.width, this.height);
if (refill) {
this.fill(this.fillColor);
}
}
/**
* @description Sauvegarde le contenu du canvas
* @param {HTMLImageElement} imageElement L'image devant afficher le contenu sauvegardé (base64)
* @param {Function} loadCallback La fonction devant être appelée lorsque le chargement de l'image est terminé
*
*@example <caption>1) Sauvegarde du contenu du canvas et placement dans une image</caption>
* let dessin = new SimpleCanvasJS(250, 250, document.querySelector("#canvasDiv"), "#ffffff", "#ff0000", 5);
* dessin.save(document.querySelector('img'))
*
* @example <caption>2) Sauvegarde du contenu du canvas et placement dans une image avec fonction appelée lorsque tout est chargé</caption>
* let dessin = new SimpleCanvasJS(250, 250, document.querySelector("#canvasDiv"), "#ffffff", "#ff0000", 5);
* dessin.save(document.querySelector('img'), function(){
* console.log("Chargement de l'image complété");
* })
*
* @returns {string | undefined} Retourne l'image en chaîne de caractère base64 ou undefined si une image est passée en paramètre
*/
save(imageElement, loadCallback) {
if (imageElement) {
imageElement.onload = loadCallback || null;
imageElement.src = this.canvas.toDataURL();
imageElement.width = this.width;
imageElement.height = this.height;
} else {
return this.canvas.toDataURL();
}
}
/**
* @description Détruit l'instance SimpleCanvasJS créé
*
* <br><br><em><b>Note</b>: Afin de détruire le canvas, la variable de l'instance devrait aussi être nullifiée.</em>
* @example <caption>Destruction d'un SimpleCanvasJS associé à une variable</caption>
* dessin.destroy();
* dessin = null //nullifie l'instance
*
* //ou en une seule ligne
*
* dessin = dessin.destroy()
* @returns null
*/
destroy() {
//enlever le canvas du DOM
this.canvas.parentNode.removeChild(this.canvas);
this.brush = this.#destroyed;
this.fill = this.#destroyed;
this.clear = this.#destroyed;
this.save = this.#destroyed;
delete this;
return null;
}
/*******************************************************************
*************************** PRIVATES **************************
*******************************************************************/
#setupEvents() {
this.#pointerDown = this.#onPointerDown.bind(this);
this.#pointerMove = this.#onPointerMove.bind(this);
this.#pointerUp = this.#onPointerUp.bind(this);
this.#pointerMoveOutside = this.#onPointerMoveOutside.bind(this);
this.#pointerUpOutside = this.#onPointerUpOutside.bind(this);
this.canvas.onmousedown = this.canvas.ontouchstart = this.#pointerDown;
// this.canvas.addEventListener("touchstart", this.#pointerDown, { passive: false });
}
#pointerDown = null;
#onPointerDown(e) {
this.drawing = true;
let penXPos = e.type == "touchstart" ? e.touches[0].pageX : e.pageX;
let penYPos = e.type == "touchstart" ? e.touches[0].pageY : e.pageY;
// document.body.classList.add("noscroll");
this.lastXPos = penXPos - this.canvas.offsetLeft;
this.lastYPos = penYPos - this.canvas.offsetTop;
document.body.onmousemove = document.body.ontouchmove = this.#pointerMoveOutside;
this.canvas.onmousemove = this.canvas.ontouchmove = this.#pointerMove;
this.canvas.onmouseup = this.canvas.ontouchend = this.#pointerUp;
document.body.onmouseup = document.body.ontouchend = this.#pointerUpOutside;
if (e.type == "touchstart") {
e.stopPropagation();
e.preventDefault();
}
}
#pointerMove = null;
#onPointerMove(e) {
if (this.drawing) {
this.context.beginPath();
this.context.moveTo(this.lastXPos, this.lastYPos);
let penXPos = e.type == "touchmove" ? e.touches[0].pageX : e.pageX;
let penYPos = e.type == "touchmove" ? e.touches[0].pageY : e.pageY;
let xPos = (this.lastXPos = penXPos - this.canvas.offsetLeft);
let yPos = (this.lastYPos = penYPos - this.canvas.offsetTop);
this.context.lineTo(xPos, yPos);
this.context.closePath();
this.context.stroke();
if (e.type == "touchmove") {
e.stopPropagation();
e.preventDefault();
}
}
}
#pointerMoveOutside = null;
#onPointerMoveOutside(e) {
if (this.drawing) {
if (e.target != this.canvas) {
this.canvas.onmousemove = this.canvas.ontouchmove = null;
document.body.onmousemove = document.body.ontouchmove = null;
}
}
if (e.type == "touchmove") {
e.stopPropagation();
e.preventDefault();
}
}
#pointerUp = null;
#onPointerUp(e) {
this.drawing = false;
// document.body.classList.remove("noscroll");
this.canvas.onmousemove = this.canvas.ontouchmove = null;
document.body.onmousemove = document.body.ontouchmove = null;
this.canvas.onmouseup = this.canvas.ontouchend = null;
}
#pointerUpOutside = null;
#onPointerUpOutside(e) {
this.drawing = false;
//document.body.classList.remove("noscroll");
this.canvas.onmousemove = this.canvas.ontouchmove = null;
document.body.onmousemove = document.body.ontouchmove = null;
this.canvas.onmouseup = this.canvas.ontouchend = null;
}
#destroyed() {
console.warn("Le canvas a été détruit. Il est impossible de l'utiliser.");
}
}
export default SimpleCanvasJS;