@alfalab/react-canvas-pattern-lock
Version:
Android's react pattern lock
760 lines (744 loc) • 32.5 kB
JavaScript
'use strict';
var React = require('react');
var mergeRefs = require('react-merge-refs');
var useEvent = require('react-use-event-hook');
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
var React__default = /*#__PURE__*/_interopDefaultCompat(React);
var mergeRefs__default = /*#__PURE__*/_interopDefaultCompat(mergeRefs);
var useEvent__default = /*#__PURE__*/_interopDefaultCompat(useEvent);
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __spreadArray(to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
}
var unregisterEvent = function (target, event, fn) { return (target ? event.split(' ').forEach(function (ev) { return target.removeEventListener(ev, fn); }) : null); };
var registerEvent = function (target, event, fn) {
if (target) {
event.split(' ').forEach(function (ev) { return target.addEventListener(ev, fn, { passive: false }); });
return function () { return unregisterEvent(target, event, fn); };
}
return function () { };
};
/* eslint-disable no-multi-assign */
var EventBus = function () {
var eventMap = {};
var off = function (eventName, cb) {
var fns = (eventMap[eventName] = eventMap[eventName] || []);
// eslint-disable-next-line no-bitwise
return fns.splice(fns.indexOf(cb) >>> 0, 1);
};
var on = function (eventName, cb) {
var fns = (eventMap[eventName] = eventMap[eventName] || []);
fns.push(cb);
return off.bind(null, eventName, cb);
};
var emit = function (event) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
var fns = eventMap[event];
if (!fns || !fns.length)
return [];
return fns.map(function (fn) { return fn.apply(void 0, args); });
};
return { on: on, off: off, emit: emit };
};
var gcd = function (x, y) {
while (y !== 0) {
var tmp = x;
x = y;
y = tmp % y;
}
return x;
};
var prop = function (path, obj) { return path.split('.').reduce(function (acc, key) { return (acc ? acc[key] : undefined); }, obj); };
function generateKey(row, cell) {
return "".concat(row, "-").concat(cell);
}
/**
* Формирует объект соответствия точки ключа и значения.
*/
function buildDotToValueMap(grid) {
var res = {};
var currentValue = 1;
for (var i = 1; i <= grid[0]; i += 1) {
for (var j = 1; j <= grid[1]; j += 1) {
res[generateKey(i, j)] = currentValue;
currentValue += 1;
}
}
return res;
}
/**
* Формирует цифровой код для сетки grid из списка выбранных точек графического ключа.
*/
function nodesToCode(nodes, grid) {
var rowColMap = buildDotToValueMap(grid);
return nodes.map(function (node) { return rowColMap[generateKey(node.row, node.col)]; });
}
var _a;
var DEFAULT_EXTRA_BOUNDS = [25, 25, 25, 25];
var JUSTIFY_NODES_OPTION = {
SPACE_AVAILABLE: 'space-around',
SPACE_BETWEEN: 'space-between',
};
var DEFAULT_THEME_STATE = {
SUCCESS: 'success',
FAILURE: 'failure',
INITIAL: 'initial',
};
var dimens = {
nodeRadius: 37.5,
lineWidth: 6,
nodeCore: 12.5,
};
var baseColors = {
primary: '#c5c5c7',
bg: '#fff',
ringBg: 'rgba(11, 31, 53, 0.05)',
hover: {
inner: '#a0a1a9',
outer: 'rgba(15, 25, 55, 0.1)',
},
};
var DEFAULT_LIGHT_THEME = (_a = {},
_a[DEFAULT_THEME_STATE.INITIAL] = {
colors: __assign(__assign({}, baseColors), { accent: '#000', selectedRingBg: 'rgba(11, 31, 53, 0.1)' }),
dimens: dimens,
},
_a[DEFAULT_THEME_STATE.SUCCESS] = {
colors: __assign(__assign({}, baseColors), { accent: '#2FC26E', selectedRingBg: '#eaf9f0' }),
dimens: dimens,
},
_a[DEFAULT_THEME_STATE.FAILURE] = {
colors: __assign(__assign({}, baseColors), { accent: '#F15045', selectedRingBg: '#feedeb' }),
dimens: dimens,
},
_a);
var createInvalidOptionError = function (option) { return new Error("Invalid or empty ".concat(option, " passed")); };
var events = {
PATTERN_COMPLETE: 'complete',
PATTERN_START: 'start',
};
var PatternLock = /** @class */ (function () {
function PatternLock(config) {
var _this = this;
this._subscriptions = [];
this.eventBus = EventBus();
this.theme = DEFAULT_LIGHT_THEME;
this.themeState = DEFAULT_LIGHT_THEME[DEFAULT_THEME_STATE.INITIAL];
this.justifyNodes = 'space-around';
this.dimens = { width: 300, height: 400 };
this.coordinates = null;
this.selectedNodes = [];
this.hoveredNode = null;
this.hoveredAt = 0;
this.lastSelectedNode = null;
this._isDragging = false;
this.rows = 3;
this.cols = 3;
this.renderLoopRaf = 0;
this.calculationLoopRaf = 0;
this.resetTimeoutId = 0;
this.dragListeners = [];
this.bounds = { x: 0, y: 0 };
this.extraBounds = [0, 0, 0, 0];
this.hover = false;
this.setThemeRaf = 0;
this.registerEventListener = function (t, ev, fn) {
var unsubFn = registerEvent(t, ev, fn);
_this._subscriptions.push(unsubFn);
return unsubFn;
};
// Event handler stuff start
this.destroy = function () {
_this._stopDragging();
_this._subscriptions.map(function (fn) { return fn(); });
};
this._stopDragging = function () {
cancelAnimationFrame(_this.renderLoopRaf);
cancelAnimationFrame(_this.calculationLoopRaf);
cancelAnimationFrame(_this.setThemeRaf);
clearTimeout(_this.resetTimeoutId);
_this._isDragging = false;
_this.hoveredAt = 0;
};
this.emit = function (eventName) {
var _a;
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
return (_a = _this.eventBus).emit.apply(_a, __spreadArray([eventName], args, false));
};
this.onStart = function (fn) { return _this.on(events.PATTERN_START, fn); };
this.onComplete = function (fn) { return _this.on(events.PATTERN_COMPLETE, fn); };
this._getCoords = function (col, row) {
var x = 0;
var y = 0;
if (_this.justifyNodes === JUSTIFY_NODES_OPTION.SPACE_AVAILABLE) {
var xPartsCount = _this.dimens.width / _this.cols;
var yPartsCount = _this.dimens.height / _this.rows;
x = xPartsCount * col - xPartsCount / 2;
y = yPartsCount * row - yPartsCount / 2;
}
if (_this.justifyNodes === JUSTIFY_NODES_OPTION.SPACE_BETWEEN) {
var nodeRadius = _this.themeState.dimens.nodeRadius;
var nodeSize = nodeRadius * 2;
var xSpace = _this.dimens.width - nodeSize * _this.cols;
var xSpaceAfterNode = xSpace / (_this.cols - 1);
var ySpace = _this.dimens.height - nodeSize * _this.rows;
var ySpaceAfterNode = ySpace / (_this.rows - 1);
x = nodeRadius + (col - 1) * nodeSize + (col - 1) * xSpaceAfterNode;
y = nodeRadius + (row - 1) * nodeSize + (row - 1) * ySpaceAfterNode;
}
return { x: x, y: y };
};
this._getMousePointFromEvent = function (e) {
var mousePoint = {
x: prop('pageX', e) || prop('touches.0.pageX', e) || 0,
y: prop('pageY', e) || prop('touches.0.pageY', e) || 0,
};
return {
x: mousePoint.x - _this.bounds.x,
y: mousePoint.y - _this.bounds.y,
};
};
this._findPosByCoord = function (coord, dimen, nodesCount) {
if (_this.justifyNodes === JUSTIFY_NODES_OPTION.SPACE_AVAILABLE) {
var partsCount = dimen / nodesCount;
return Math.round((coord + partsCount / 2) / partsCount);
}
if (_this.justifyNodes === JUSTIFY_NODES_OPTION.SPACE_BETWEEN) {
var nodeRadius = _this.themeState.dimens.nodeRadius;
var nodeSize = nodeRadius * 2;
var space = dimen - nodeSize * nodesCount;
var spaceAfterNode = space / (nodesCount - 1);
return Math.round((coord - nodeRadius + nodeSize + spaceAfterNode) / (nodeSize + spaceAfterNode));
}
return 0;
};
this._findColByX = function (x) { return _this._findPosByCoord(x, _this.dimens.width, _this.cols); };
this._findRowByY = function (y) { return _this._findPosByCoord(y, _this.dimens.height, _this.rows); };
this._emitPatternStart = function () { return _this.emit(events.PATTERN_START, {}); };
// Event handler stuff end
// recalculateBounds :: () -> Point
this.recalculateBounds = function () {
var bodyRect = document.body.getBoundingClientRect();
var elemRect = _this.$canvas.getBoundingClientRect();
var offset = elemRect.top - bodyRect.top;
_this.bounds = { x: elemRect.left, y: offset };
};
this._onResize = function () { return requestAnimationFrame(_this.recalculateBounds); };
this._startListenMouse = function () {
// Start frame loops
_this.renderLoopRaf = requestAnimationFrame(_this.renderLoop);
_this.calculationLoopRaf = requestAnimationFrame(_this.calculationLoop);
_this.dragListeners = [
_this.registerEventListener(window, 'mousemove touchmove', _this._onTouchMove),
_this.registerEventListener(window, 'mouseup touchend', _this._onTouchStop),
];
};
this._onTouchStart = function (e) {
if (!_this.hover) {
_this._stopDragging();
_this._startListenMouse();
}
requestAnimationFrame(_this.recalculateBounds);
if (e)
e.preventDefault();
var mousePoint = _this._getMousePointFromEvent(e);
if (_this.isPointInCanvas(mousePoint)) {
_this.coordinates = mousePoint;
}
_this.setInitialState();
_this._emitPatternStart();
_this._isDragging = true;
};
this._onTouchStop = function (e) {
if (e)
e.preventDefault();
if (_this.hover) {
_this.renderLoop(false);
_this._isDragging = false;
if (_this.selectedNodes.length === 1) {
_this.selectedNodes = [];
}
if (_this.selectedNodes.length > 1) {
_this._emitPatternComplete();
}
return;
}
(_this.dragListeners || []).forEach(function (fn) { return fn(); });
_this._subscriptions = _this._subscriptions.filter(function (fn) { return !(_this.dragListeners || []).includes(fn); });
_this.coordinates = null;
_this.renderLoop(false);
if (_this.selectedNodes.length > 1) {
_this._emitPatternComplete();
}
_this._stopDragging();
};
this._onTouchMove = function (e) {
if (e)
e.preventDefault();
if (_this._isDragging) {
var mousePoint = _this._getMousePointFromEvent(e);
if (_this.isPointInCanvas(mousePoint)) {
_this.coordinates = mousePoint;
}
else {
_this._onTouchStop();
}
}
if (!_this._isDragging && _this.hover) {
var mousePoint = _this._getMousePointFromEvent(e);
if (_this.isPointInCanvas(mousePoint)) {
_this.coordinates = mousePoint;
}
else {
_this.coordinates = null;
}
}
};
/*
* Checks if given point is within the boundaries of the canvas
* isPointInCanvas :: Point -> Boolean
*/
this.isPointInCanvas = function (_a) {
var x = _a.x, y = _a.y;
var w = _this.dimens.width;
var h = _this.dimens.height;
var _b = _this.extraBounds, top = _b[0], right = _b[1], bottom = _b[2], left = _b[3];
return x <= w + right && x > 0 - left && y <= h + bottom && y > 0 - top;
};
/*
* Check if the given node is already selected
*/
this.isSelected = function (targetNode) { return _this.selectedNodes.some(function (node) { return node.col === targetNode.col && node.row === targetNode.row; }); };
this.isHovered = function (targetNode) {
var _a, _b;
var t = targetNode;
return ((_a = _this.hoveredNode) === null || _a === void 0 ? void 0 : _a.row) === t.row && ((_b = _this.hoveredNode) === null || _b === void 0 ? void 0 : _b.col) === t.col;
};
// eslint-disable-next-line class-methods-use-this
this.getElapsedTime = function (value, startTime) {
var timeStamp;
if (typeof value === 'boolean') {
timeStamp = performance.now();
}
else {
timeStamp = value;
}
return timeStamp - startTime;
};
// Calculate the state of the lock for the next frame
this.calculationLoop = function (runLoop) {
if (runLoop === void 0) { runLoop = true; }
var dimens = _this.themeState.dimens;
var isDrag = Boolean(_this._isDragging && _this.coordinates);
var isHover = Boolean(_this.hover && !_this._isDragging && _this.coordinates);
if (isDrag || isHover) {
// eslint-disable-next-line consistent-return
_this.forEachNode(function (x, y) {
var dist = Math.sqrt(Math.pow((_this.coordinates.x - x), 2) + Math.pow((_this.coordinates.y - y), 2));
var col = _this._findColByX(x);
var row = _this._findRowByY(y);
var currentNode = { col: col, row: row };
if (dist < dimens.nodeRadius + 1) {
if (isDrag && !_this.isSelected(currentNode)) {
_this.addIntermediaryNodes(currentNode);
_this.selectedNodes.push(currentNode);
return false;
}
if (isHover && !_this.isSelected(currentNode)) {
if (!_this.isHovered(currentNode)) {
_this.hoveredNode = currentNode;
_this.hoveredAt = performance.now();
return false;
}
}
}
else if (_this.hover && _this.isHovered(currentNode)) {
_this.hoveredNode = null;
return false;
}
});
}
if (runLoop) {
_this.calculationLoopRaf = requestAnimationFrame(_this.calculationLoop);
}
};
// Render the state of the lock
this.renderLoop = function (runLoop) {
if (runLoop === void 0) { runLoop = true; }
if (_this._isDragging) {
// Paint the grid
_this.renderGrid();
_this.drawSelected();
}
if (_this.hover && !_this._isDragging) {
// Paint the grid
_this.renderGrid();
_this.drawSelected();
if (_this.hoveredNode) {
_this.drawHoveredNode(_this.getElapsedTime(runLoop, _this.hoveredAt));
}
}
if (runLoop) {
_this.renderLoopRaf = requestAnimationFrame(_this.renderLoop);
}
};
this.drawSelected = function () {
var _a = _this.themeState.colors, accent = _a.accent, primary = _a.primary, selectedRingBg = _a.selectedRingBg;
var nodesCount = _this.selectedNodes.length;
var drawNodes = function () {
var lastPointCoords;
for (var i = 0; i < nodesCount; i += 1) {
var prev = _this.selectedNodes[i - 1];
var curr = _this.selectedNodes[i];
var pointCoords = _this._getCoords(curr.col, curr.row);
var isLastNode = nodesCount - 1 === i;
if (isLastNode) {
lastPointCoords = pointCoords;
}
if (curr && prev) {
var prevPointCoords = _this._getCoords(prev.col, prev.row);
if (!_this.coordinates && isLastNode) {
_this.drawNode(pointCoords.x, pointCoords.y, accent, primary, selectedRingBg);
}
_this.drawNode(prevPointCoords.x, prevPointCoords.y, accent, primary, selectedRingBg);
}
}
if (lastPointCoords && _this.coordinates) {
_this.drawNode(lastPointCoords.x, lastPointCoords.y, accent, primary, selectedRingBg);
}
};
var drawLines = function () {
var lastPointCoords;
for (var i = 0; i < nodesCount; i += 1) {
var prev = _this.selectedNodes[i - 1];
var curr = _this.selectedNodes[i];
var pointCoords = _this._getCoords(curr.col, curr.row);
var isLastNode = nodesCount - 1 === i;
if (isLastNode) {
lastPointCoords = pointCoords;
}
if (curr && prev) {
_this.joinNodes(prev.col, prev.row, curr.col, curr.row, false);
}
}
if (_this._isDragging && lastPointCoords && _this.coordinates) {
// Draw a line between last node to the current drag position
_this.joinNodes(lastPointCoords.x, lastPointCoords.y, _this.coordinates.x, _this.coordinates.y, true);
}
};
drawNodes();
// draw lines over nodes
drawLines();
};
if (!config.$canvas)
throw createInvalidOptionError('$canvas');
this.initialize(config);
}
PatternLock.prototype.initialize = function (config) {
var $canvas = config.$canvas, grid = config.grid, theme = config.theme, width = config.width, height = config.height, themeStateKey = config.themeStateKey, justifyNodes = config.justifyNodes, extraBounds = config.extraBounds, hover = config.hover;
this._initialConfig = config;
this.$canvas = $canvas;
this.ctx = this.$canvas.getContext('2d');
this.theme = theme;
this.themeState = theme[themeStateKey];
this.justifyNodes = justifyNodes;
this.extraBounds = extraBounds;
this.hover = hover;
this.setDimensions({ width: width, height: height });
this.setGrid(grid[0], grid[1]);
this.attachEventHandlers();
};
PatternLock.prototype.setDimensions = function (dimens) {
this.dimens = dimens;
var ratio = window.devicePixelRatio;
this.$canvas.width = this.dimens.width * ratio;
this.$canvas.height = this.dimens.height * ratio;
this.$canvas.style.width = "".concat(this.dimens.width, "px");
this.$canvas.style.height = "".concat(this.dimens.height, "px");
this.ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
};
PatternLock.prototype.setInitialState = function () {
this.selectedNodes = [];
this.lastSelectedNode = null;
this.themeState = this.theme[this._initialConfig.themeStateKey];
this.renderGrid();
};
PatternLock.prototype.setGrid = function (rows, cols) {
this.rows = rows;
this.cols = cols;
this.setInitialState();
this._onResize();
return this;
};
PatternLock.prototype.setThemeState = function (themeState, rerender) {
var _this = this;
if (rerender === void 0) { rerender = true; }
if (!this.theme)
throw createInvalidOptionError('theme');
this.themeState = this.theme[themeState || DEFAULT_THEME_STATE.INITIAL] || {};
this.themeState.colors = __assign(__assign({}, this.theme.initial.colors), this.themeState.colors);
this.themeState.dimens = __assign(__assign({}, this.theme.initial.dimens), this.themeState.dimens);
if (rerender) {
this.setThemeRaf = requestAnimationFrame(function () {
_this.renderGrid();
_this.drawSelected();
});
}
return this;
};
// Attach event listeners and start frame loops
PatternLock.prototype.attachEventHandlers = function () {
this.registerEventListener(this.$canvas, 'mousedown touchstart', this._onTouchStart);
this.registerEventListener(window, 'resize', this._onResize);
if (this.hover) {
this._startListenMouse();
}
};
PatternLock.prototype.on = function (event, fn) {
var subscription = this.eventBus.on(event, fn);
this._subscriptions.push(subscription);
return subscription;
};
PatternLock.prototype._emitPatternComplete = function () {
this.emit(events.PATTERN_COMPLETE, this.selectedNodes);
};
/*
* Adds intermediary nodes between lastSelectedNode and the target
*/
PatternLock.prototype.addIntermediaryNodes = function (target) {
var stepNode = this.getIntermediaryStepDirection(this.lastSelectedNode, target);
if (this.lastSelectedNode && (stepNode.col !== 0 || stepNode.row !== 0)) {
var current = {
col: this.lastSelectedNode.col + stepNode.col,
row: this.lastSelectedNode.row + stepNode.row,
};
var max = Math.max(this.rows, this.cols);
var i = 0;
// eslint-disable-next-line no-plusplus
while (i++ < max && (current.col !== target.col || current.row !== target.row)) {
if (!this.isSelected(current)) {
this.selectedNodes.push(current);
}
current = {
col: current.col + stepNode.col,
row: current.row + stepNode.row,
};
}
}
this.lastSelectedNode = target;
};
/*
* Returns the step direction to select intermediary nodes
* INFO: Can be moved out of the class as it is independent of `this`
* getIntermediaryStepDirection :: (Node, Node) -> Node
*/
// eslint-disable-next-line class-methods-use-this
PatternLock.prototype.getIntermediaryStepDirection = function (prev, next) {
var finalStep = { col: 0, row: 0 };
if (!prev) {
return finalStep;
}
var dRow = Math.abs(prev.col - next.col);
var dCol = Math.abs(prev.row - next.row);
if (dRow === 1 || dCol === 1) {
return finalStep;
}
var dRsign = prev.col - next.col < 0 ? 1 : -1;
var dCsign = prev.row - next.row < 0 ? 1 : -1;
if (dRow === 0) {
if (dCol !== 0) {
finalStep.row = dCsign;
}
}
else if (dCol === 0) {
finalStep.col = dRsign;
}
else {
var max = Math.max(dRow, dCol);
var min = Math.min(dRow, dCol);
var gcdValue = gcd(max, min);
if (max % min === 0) {
finalStep.row = (dCol / gcdValue) * dCsign;
finalStep.col = (dRow / gcdValue) * dRsign;
}
}
return finalStep;
};
// Render the grid to the canvas
PatternLock.prototype.renderGrid = function () {
this.ctx.fillStyle = this.themeState.colors.bg;
this.ctx.clearRect(0, 0, this.dimens.width, this.dimens.height);
this.ctx.fillRect(0, 0, this.dimens.width, this.dimens.height);
// Draw all the nodes
this.forEachNode(this.drawNode.bind(this));
};
PatternLock.prototype.forEachNode = function (callback) {
var _this = this;
var xGrid = Array(this.cols)
.fill(null)
.map(function (el, i) { return _this._getCoords(i + 1, 0).x; });
var yGrid = Array(this.rows)
.fill(null)
.map(function (el, i) { return _this._getCoords(0, i + 1).y; });
var breakException = new Error('Break Exception');
try {
yGrid.forEach(function (y) {
xGrid.forEach(function (x) {
if (callback(x, y) === false)
throw breakException;
});
});
}
catch (e) {
if (e !== breakException)
throw e;
}
};
PatternLock.prototype.drawHoveredNode = function (elapsed) {
var _a = this.themeState.colors.hover, inner = _a.inner, outer = _a.outer;
var _b = this._getCoords(this.hoveredNode.col, this.hoveredNode.row), x = _b.x, y = _b.y;
var alpha = 1 - ((100 - elapsed) / 100);
this.drawNode(x, y, inner, outer, outer, alpha);
};
PatternLock.prototype.drawNode = function (x, y, centerColor, borderColor, ringBgColor, alpha) {
if (alpha === void 0) { alpha = 1; }
var _a = this.themeState, _b = _a.dimens, ringRadius = _b.nodeRadius, coreRadius = _b.nodeCore, _c = _a.colors, primary = _c.primary, ringBg = _c.ringBg;
// Config
this.ctx.strokeStyle = borderColor || primary;
this.ctx.fillStyle = ringBgColor || ringBg;
// clear circle
this.ctx.globalCompositeOperation = 'destination-out';
this.ctx.beginPath();
this.ctx.arc(x, y, ringRadius, 0, Math.PI * 2);
this.ctx.fill();
this.ctx.globalCompositeOperation = 'source-over';
this.ctx.globalAlpha = Math.max(alpha, 0);
// Draw outer circle.
this.ctx.beginPath();
this.ctx.arc(x, y, ringRadius, 0, Math.PI * 2);
this.ctx.fill();
// Draw inner circle
this.ctx.fillStyle = centerColor || primary;
this.ctx.globalAlpha = alpha > 0.25 ? 1 : Math.max(alpha + 0.75, 0);
this.ctx.beginPath();
this.ctx.arc(x, y, coreRadius, 0, Math.PI * 2);
this.ctx.fill();
this.ctx.globalAlpha = 1;
};
PatternLock.prototype.joinNodes = function (col1, row1, col2, row2, isCoordinates, alpha) {
if (isCoordinates === void 0) { isCoordinates = false; }
if (alpha === void 0) { alpha = 1; }
this.ctx.globalAlpha = Math.max(alpha, 0);
var coords1 = this._getCoords(col1, row1);
var coords2 = this._getCoords(col2, row2);
var point1 = { x: coords1.x, y: coords1.y };
var point2 = { x: coords2.x, y: coords2.y };
if (isCoordinates) {
point1 = { x: col1, y: row1 };
point2 = { x: col2, y: row2 };
}
this.ctx.lineWidth = this.themeState.dimens.lineWidth;
this.ctx.strokeStyle = this.themeState.colors.accent;
this.ctx.lineCap = 'round';
// Draw line
this.ctx.beginPath();
this.ctx.moveTo(point1.x, point1.y);
this.ctx.lineTo(point2.x, point2.y);
this.ctx.stroke();
this.ctx.globalAlpha = 1;
};
return PatternLock;
}());
var useLayoutEffectSafeForSsr = typeof document !== 'undefined' ? React.useLayoutEffect : React.useEffect;
var ReactCanvasPatternLock = React.forwardRef(function (_a, ref) {
var _b = _a.width, width = _b === void 0 ? 315 : _b, _c = _a.height, height = _c === void 0 ? 315 : _c, onComplete = _a.onComplete, themeState = _a.themeState, onDragStart = _a.onDragStart, _d = _a.theme, theme = _d === void 0 ? DEFAULT_LIGHT_THEME : _d, _e = _a.justifyNodes, justifyNodes = _e === void 0 ? 'space-around' : _e, _f = _a.rows, rows = _f === void 0 ? 3 : _f, _g = _a.cols, cols = _g === void 0 ? 3 : _g, _h = _a.extraBounds, extraBounds = _h === void 0 ? DEFAULT_EXTRA_BOUNDS : _h, _j = _a.hover, hover = _j === void 0 ? false : _j;
var canvasRef = React.useRef(null);
var patternLockInnerRef = React.useRef();
var handleComplete = useEvent__default.default(function (nodes) {
if (nodes === null || nodes === void 0 ? void 0 : nodes.length) {
onComplete === null || onComplete === void 0 ? void 0 : onComplete(nodesToCode(nodes, [rows, cols]), nodes);
}
});
var handleDragStart = useEvent__default.default(function () {
onDragStart === null || onDragStart === void 0 ? void 0 : onDragStart();
if (!themeState && patternLockInnerRef.current) {
patternLockInnerRef.current.setThemeState(DEFAULT_THEME_STATE.INITIAL);
}
});
useLayoutEffectSafeForSsr(function () {
var patternLockVar = patternLockInnerRef;
if (canvasRef.current) {
mergeRefs__default.default([ref, patternLockVar])(new PatternLock({
$canvas: canvasRef.current,
width: width,
height: height,
grid: [rows, cols],
theme: theme,
themeStateKey: themeState || DEFAULT_THEME_STATE.INITIAL,
justifyNodes: justifyNodes,
extraBounds: extraBounds,
hover: hover,
}));
if (patternLockVar.current) {
patternLockVar.current.onComplete(handleComplete);
patternLockVar.current.onStart(handleDragStart);
}
}
return function () {
var _a;
(_a = patternLockVar.current) === null || _a === void 0 ? void 0 : _a.destroy();
patternLockInnerRef.current = undefined;
};
}, [
width,
height,
justifyNodes,
rows,
cols,
ref,
theme,
hover,
handleComplete,
handleDragStart,
]);
React.useEffect(function () {
if (themeState && patternLockInnerRef.current) {
patternLockInnerRef.current.setThemeState(themeState);
}
}, [themeState]);
return React__default.default.createElement("canvas", { ref: canvasRef });
});
exports.ReactCanvasPatternLock = ReactCanvasPatternLock;