I’m trying to build the most smooth and pen like canvas drawing app using javascript.
I have tried many libs and codes but they’re not that smooth! Finally I have found this project written in typescript and by far it’s the best solution available and it’s really smooth.
https://github.com/dulnan/lazy-brush?tab=readme-ov-file
Now the issue:
Let’s say we have a canvas and we want to implement the above code on it.
Here is our canvas:
<canvas class="canvas" width="1560" height="933"></canvas>
The issue is the project doesn’t come with distribution file ready for production to plug it in my page and start using it. And no matter how I try I’m unable to make it work correctly.
Here is the two pieces of the lib code in src folder converted to the JS:
LazyBrush.ts converted to JS:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var LazyPoint_1 = require("./LazyPoint");
var RADIUS_DEFAULT = 30;
var LazyBrush = /** @class */ (function () {
/**
* Constructs a new LazyBrush.
*/
function LazyBrush(options) {
if (options === void 0) { options = {}; }
var initialPoint = options.initialPoint || { x: 0, y: 0 };
this.radius = options.radius || RADIUS_DEFAULT;
this._isEnabled = options.enabled === false ? false : true;
this.pointer = new LazyPoint_1.LazyPoint(initialPoint.x, initialPoint.y);
this.brush = new LazyPoint_1.LazyPoint(initialPoint.x, initialPoint.y);
this.angle = 0;
this.distance = 0;
this._hasMoved = false;
}
/**
* Enable lazy brush calculations.
*
*/
LazyBrush.prototype.enable = function () {
this._isEnabled = true;
};
/**
* Disable lazy brush calculations.
*
*/
LazyBrush.prototype.disable = function () {
this._isEnabled = false;
};
/**
* @returns {boolean}
*/
LazyBrush.prototype.isEnabled = function () {
return this._isEnabled;
};
/**
* Update the radius
*
* @param {number} radius
*/
LazyBrush.prototype.setRadius = function (radius) {
this.radius = radius;
};
/**
* Return the current radius
*
* @returns {number}
*/
LazyBrush.prototype.getRadius = function () {
return this.radius;
};
/**
* Return the brush coordinates as a simple object
*
* @returns {object}
*/
LazyBrush.prototype.getBrushCoordinates = function () {
return this.brush.toObject();
};
/**
* Return the pointer coordinates as a simple object
*
* @returns {object}
*/
LazyBrush.prototype.getPointerCoordinates = function () {
return this.pointer.toObject();
};
/**
* Return the brush as a LazyPoint
*
* @returns {LazyPoint}
*/
LazyBrush.prototype.getBrush = function () {
return this.brush;
};
/**
* Return the pointer as a LazyPoint
*
* @returns {LazyPoint}
*/
LazyBrush.prototype.getPointer = function () {
return this.pointer;
};
/**
* Return the angle between pointer and brush
*
* @returns {number} Angle in radians
*/
LazyBrush.prototype.getAngle = function () {
return this.angle;
};
/**
* Return the distance between pointer and brush
*
* @returns {number} Distance in pixels
*/
LazyBrush.prototype.getDistance = function () {
return this.distance;
};
/**
* Return if the previous update has moved the brush.
*
* @returns {boolean} Whether the brush moved previously.
*/
LazyBrush.prototype.brushHasMoved = function () {
return this._hasMoved;
};
/**
* Updates the pointer point and calculates the new brush point.
*/
LazyBrush.prototype.update = function (newPointerPoint, options) {
if (options === void 0) { options = {}; }
this._hasMoved = false;
if (this.pointer.equalsTo(newPointerPoint) &&
!options.both &&
!options.friction) {
return false;
}
this.pointer.update(newPointerPoint);
if (options.both) {
this._hasMoved = true;
this.brush.update(newPointerPoint);
return true;
}
if (this._isEnabled) {
this.distance = this.pointer.getDistanceTo(this.brush);
this.angle = this.pointer.getAngleTo(this.brush);
var isOutside = Math.round((this.distance - this.radius) * 10) / 10 > 0;
var friction = options.friction && options.friction < 1 && options.friction > 0
? options.friction
: undefined;
if (isOutside) {
this.brush.moveByAngle(this.angle, this.distance - this.radius, friction);
this._hasMoved = true;
}
}
else {
this.distance = 0;
this.angle = 0;
this.brush.update(newPointerPoint);
this._hasMoved = true;
}
return true;
};
return LazyBrush;
}());
exports.default = LazyBrush;
LazyPoint.ts converted to JS:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LazyPoint = void 0;
function ease(x) {
return 1 - Math.sqrt(1 - Math.pow(x, 2));
}
var LazyPoint = /** @class */ (function () {
function LazyPoint(x, y) {
this.x = x;
this.y = y;
}
/**
* Update the x and y values
*/
LazyPoint.prototype.update = function (point) {
this.x = point.x;
this.y = point.y;
return this;
};
/**
* Move the point to another position using an angle and distance
*/
LazyPoint.prototype.moveByAngle = function (
// The angle in radians
angle,
// How much the point should be moved
distance,
// How much of the required distance the coordinates are moved. A value of
// 1 means the full distance is moved. A lower value reduces the distance
// and makes the brush more sluggish.
friction) {
// Rotate the angle based on the browser coordinate system ([0,0] in the top left)
var angleRotated = angle + Math.PI / 2;
if (friction) {
this.x = this.x + Math.sin(angleRotated) * distance * ease(1 - friction);
this.y = this.y - Math.cos(angleRotated) * distance * ease(1 - friction);
}
else {
this.x = this.x + Math.sin(angleRotated) * distance;
this.y = this.y - Math.cos(angleRotated) * distance;
}
return this;
};
/**
* Check if this point is the same as another point
*/
LazyPoint.prototype.equalsTo = function (point) {
return this.x === point.x && this.y === point.y;
};
/**
* Get the difference for x and y axis to another point
*/
LazyPoint.prototype.getDifferenceTo = function (point) {
return new LazyPoint(this.x - point.x, this.y - point.y);
};
/**
* Calculate distance to another point
*/
LazyPoint.prototype.getDistanceTo = function (point) {
var diff = this.getDifferenceTo(point);
return Math.sqrt(Math.pow(diff.x, 2) + Math.pow(diff.y, 2));
};
/**
* Calculate the angle to another point
*/
LazyPoint.prototype.getAngleTo = function (point) {
var diff = this.getDifferenceTo(point);
return Math.atan2(diff.y, diff.x);
};
/**
* Return a simple object with x and y properties
*/
LazyPoint.prototype.toObject = function () {
return {
x: this.x,
y: this.y
};
};
return LazyPoint;
}());
exports.LazyPoint = LazyPoint;
Based on the reference on github we can initial the brush using :
const lazy = new LazyBrush({
radius: 30,
enabled: true,
initialPoint: { x: 0, y: 0 }
})
I can’t even understand how to connect my canvas to this?
Any help will be greatly appreciated