Best JavaScript code snippet using testcafe
CanvasDraw.js
Source:CanvasDraw.js
...353 _this.isDrawing = false;354 _this.isPressing = false;355 if (_this.props.bSelect) {356 _this.ctx.select.globalCompositeOperation = "source-over";357 const xs = _this.getSelectionRectangle(_this.points);358 if (xs.length == 4 && xs[0] > 0) {359 _this.handleSelectionEnd();360 var x = xs[0] | 0;361 var y = xs[1] | 0;362 var w = xs[2] | 0;363 var h = xs[3] | 0;364 if (w < 0) { x += w; w=-w; }365 if (h < 0) { y += h; h=-h; }366 if (w !== 0 && h !== 0) {367 const imageData = _this.ctx.drawing.getImageData(x, y, w, h);368 _this.imageDataToJpg(imageData);369 _this.pasteImageData = imageData;370 _this.pasteMemo.push(_this.pasteImageData);371 _this.ctx.drawing.clearRect(x, y, w, h);372 _this.sel_x = _this.orig_x = x;373 _this.sel_y = _this.orig_y = y;374 _this.sel_w = w;375 _this.sel_h = h;376 _this.ctx.select.putImageData(imageData, x, y);377 }378 }379 }380 if (_this.props.bSelect === false && _this.props.disabled === false && _this.points.length >= 2) _this.props.onDrawFinish && _this.props.onDrawFinish(_this);381 382 if (_this.props.bSmooth === true) {383 console.log("etc");384 const new_format_points = _this.points.map(result => 385 [result["x"], result["y"], 0.5]386 );387 console.log(JSON.stringify(new_format_points));388 const stroked_points = _stroke.getStroke(new_format_points, {389 size: 8,390 thinning: 0.5,391 smoothing: 0.5,392 streamline: 0.5,393 easing: (t) => t,394 simulatePressure: true,395 last: true,396 start: {397 cap: true,398 taper: 0,399 easing: (t) => t,400 },401 end: {402 cap: true,403 taper: 0,404 easing: (t) => t,405 },406 });407 console.log(JSON.stringify(stroked_points));408 // console.log(JSON.stringify(new_format_stroked_points);409 410 for (var i = 0; i < _this.points.length && i < stroked_points.length; i++)411 {412 _this.points[i].x = Math.floor(stroked_points[i][0]);413 _this.points[i].y = Math.floor(stroked_points[i][1]);414 }415 416 console.log(JSON.stringify(_this.points));417 if (_this.points.length >= 2 )418 _this.drawPoints({419 points: _this.points,420 brushColor: _this.props.brushColor,421 brushRadius: _this.props.brushRadius422 });423 }424 _this.saveLine();425 };426 _this.handleCanvasResize = function (entries, observer) {427 var saveData = _this.getSaveData();428 for (var _iterator = entries, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {429 var _ref2;430 if (_isArray) {431 if (_i >= _iterator.length) break;432 _ref2 = _iterator[_i++];433 } else {434 _i = _iterator.next();435 if (_i.done) break;436 _ref2 = _i.value;437 }438 var entry = _ref2;439 var _entry$contentRect = entry.contentRect,440 width = _entry$contentRect.width,441 height = _entry$contentRect.height;442 _this.setCanvasSize(_this.canvas.interface, width, height);443 _this.setCanvasSize(_this.canvas.drawing, width, height);444 _this.setCanvasSize(_this.canvas.temp, width, height);445 _this.setCanvasSize(_this.canvas.select, width, height);446 _this.setCanvasSize(_this.canvas.borders, width, height);447 _this.setCanvasSize(_this.canvas.grid, width, height);448 _this.drawGrid(_this.ctx.grid);449 _this.drawImage();450 _this.loop({ once: true });451 }452 _this.loadSaveData(saveData, true);453 };454 _this.setCanvasSize = function (canvas, width, height) {455 canvas.width = width;456 canvas.height = height;457 canvas.style.width = width;458 canvas.style.height = height;459 };460 _this.isMouseInside = function() {461 const e = _this.latestPosition;462 if (e) {463 var rect = _this.canvas.interface.getBoundingClientRect();464 var clientX = e.clientX;465 var clientY = e.clientY;466 if (clientX < rect.left) return false;467 if (clientX > rect.right) return false;468 if (clientY > rect.bottom) return false;469 if (clientY < rect.top) return false;470 return true;471 }472 return false;473 }474 _this.getPointerPos = function (e) {475 _this.latestPosition = e;476 var rect = _this.canvas.interface.getBoundingClientRect();477 // use cursor pos as default478 var clientX = e.clientX;479 var clientY = e.clientY;480 // use first touch if available481 if (e.changedTouches && e.changedTouches.length > 0) {482 clientX = e.changedTouches[0].clientX;483 clientY = e.changedTouches[0].clientY;484 }485 // return mouse/touch position inside canvas486 const multiply = 1 / _this.props.scale;487 return {488 x: (clientX - rect.left) * multiply,489 y: (clientY - rect.top) * multiply490 };491 };492 _this.handlePointerMove = function (x, y) {493 if (_this.props.disabled) return;494 _this.lazy.update({ x: x, y: y });495 var isDisabled = !_this.lazy.isEnabled();496 if (_this.isPressing && !_this.isDrawing || isDisabled && _this.isPressing) {497 // Start drawing and add point498 _this.isDrawing = true;499 _this.points.push(_this.getPointData());500 }501 if (_this.isDrawing) {502 // Add new point503 _this.points.push(_this.getPointData());504 _this.redo_lines = [];505 // Draw current points506 if (_this.props.bSelect);507 else508 _this.drawPoints({509 points: _this.points,510 brushColor: _this.props.eraseCanvas ? "erase" : _this.props.brushColor,511 brushRadius: _this.props.brushRadius512 });513 }514 _this.mouseHasMoved = true;515 };516 _this.drawPoints = function (_ref3) {517 var points = _ref3.points,518 brushColor = _ref3.brushColor,519 brushRadius = _ref3.brushRadius;520 if (brushColor === "move-select" || brushColor === "paste-select") {521 return;522 }523 _this.ctx.temp.lineJoin = "round";524 _this.ctx.temp.lineCap = "round";525 _this.ctx.temp.strokeStyle = (brushColor === "erase") ? "#FFFFFF" : brushColor;526 this.ctx.drawing.globalCompositeOperation = (brushColor === "erase") ? "destination-out" : "source-over";527 _this.ctx.temp.clearRect(0, 0, _this.ctx.temp.canvas.width, _this.ctx.temp.canvas.height);528 _this.ctx.temp.lineWidth = brushRadius * 2;529 var p1 = points[0];530 var p2 = points[1];531 {532 _this.ctx.temp.moveTo(p2.x, p2.y);533 _this.ctx.temp.beginPath();534 for (var i = 1, len = points.length; i < len; i++) {535 // we pick the point between pi+1 & pi+2 as the536 // end point and p1 as our control point537 var midPoint = midPointBtw(p1, p2);538 _this.ctx.temp.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y);539 p1 = points[i];540 p2 = points[i + 1];541 }542 // Draw last line as a straight line while543 // we wait for the next point to be able to calculate544 // the bezier control point545 _this.ctx.temp.lineTo(p1.x, p1.y);546 _this.ctx.temp.stroke();547 }548 };549 _this.saveLine = function () {550 var _ref4 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},551 brushColor = _ref4.brushColor,552 brushRadius = _ref4.brushRadius;553 if (_this.points.length < 2) return;554 if (_this.points[0].erase) {555 brushColor = "erase";556 }557 if (_this.points[0].select) {558 _this.points.length = 0;559 _this.ctx.temp.clearRect(0, 0, width, height);560 return;561 }562 // Save as new line563 _this.lines.push({564 points: [].concat(_this.points),565 brushColor: brushColor || _this.props.brushColor,566 brushRadius: brushRadius || _this.props.brushRadius567 });568 // Reset points array569 _this.points.length = 0;570 var width = _this.canvas.temp.width;571 var height = _this.canvas.temp.height;572 // Copy the line to the drawing canvas573 _this.ctx.drawing.drawImage(_this.canvas.temp, 0, 0, width, height);574 // Clear the temporary line-drawing canvas575 _this.ctx.temp.clearRect(0, 0, width, height);576 _this.triggerOnChange();577 };578 _this.triggerOnChange = function () {579 _this.props.onChange && _this.props.onChange(_this);580 };581 _this.clear = function () {582 _this.lines = [];583 _this.valuesChanged = true;584 _this.ctx.drawing.clearRect(0, 0, _this.canvas.drawing.width, _this.canvas.drawing.height);585 _this.ctx.temp.clearRect(0, 0, _this.canvas.temp.width, _this.canvas.temp.height);586 };587 _this.loop = function () {588 var _ref5 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},589 _ref5$once = _ref5.once,590 once = _ref5$once === undefined ? false : _ref5$once;591 _this.ctx.borders.clearRect(0, 0, _this.canvas.borders.width, _this.canvas.borders.height);592 _this.drawSelectBorder(_this.time);593 if (_this.mouseHasMoved || _this.valuesChanged) {594 var pointer = _this.lazy.getPointerCoordinates();595 var brush = _this.lazy.getBrushCoordinates();596 _this.drawInterface(_this.ctx.interface, pointer, brush);597 _this.mouseHasMoved = false;598 _this.valuesChanged = false;599 }600 if (!once) {601 setTimeout(() => {_this.time += 1}, 1);602 window.requestAnimationFrame(function () {603 _this.loop();604 });605 }606 };607 _this.drawGrid = function (ctx) {608 if (_this.props.hideGrid) return;609 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);610 ctx.beginPath();611 ctx.setLineDash([5, 1]);612 ctx.setLineDash([]);613 ctx.strokeStyle = _this.props.gridColor;614 ctx.lineWidth = 0.5;615 var gridSize = 25;616 var countX = 0;617 while (countX < ctx.canvas.width) {618 countX += gridSize;619 ctx.moveTo(countX, 0);620 ctx.lineTo(countX, ctx.canvas.height);621 }622 ctx.stroke();623 var countY = 0;624 while (countY < ctx.canvas.height) {625 countY += gridSize;626 ctx.moveTo(0, countY);627 ctx.lineTo(ctx.canvas.width, countY);628 }629 ctx.stroke();630 };631 632 _this.getSelectionRectangle = function (points) {633 var x = -100;634 var y = -100;635 var w = 10;636 var h = 10;637 if (_this.points.length > 0) {638 x = _this.points[0].x;639 y = _this.points[0].y;640 w = _this.points[_this.points.length-1].x - x;641 h = _this.points[_this.points.length-1].y - y;642 }643 return [x, y, w, h]; 644 };645 _this.drawInterface = function (ctx, pointer, brush) {646 if (_this.props.hideInterface) return;647 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);648 if (_this.props.bSelect) {649 var x = _this.getSelectionRectangle(_this.points);650 ctx.beginPath();651 ctx.globalAlpha = 0.4;652 ctx.fillRect(x[0], x[1], x[2], x[3]);653 ctx.globalAlpha = 1;654 ctx.fillStyle = "#17C69A";655 ctx.lineWidth = 5;656 ctx.stroke();657 }658 else {659 // Draw brush preview660 if (_this.isMouseInside()) {661 ctx.beginPath();662 ctx.fillStyle = this.props.eraseCanvas ? "#FFFFFF" : _this.props.brushColor;663 ctx.arc(brush.x, brush.y, _this.props.brushRadius, 0, Math.PI * 2, true);...
image-editor-component.js
Source:image-editor-component.js
...85 getSelectedIndicator() {86 return this.bitmapImage.indicators.find(i => i.highlighted);87 }88 // Necessary to get a nice rectangle of minimum 1x1. Not used in move mode which uses pixel selection.89 getSelectionRectangle() {90 if (!this.rectStart) return null;91 let [x0, y0] = this.rectStart, [x1, y1] = this.rectEnd;92 return makeSelectionRectangle(x0, y0, x1, y1, this.tool === 'move' ? 0 : 1);93 }94 getSuggestedPastePosition() {95 const start = this.posInTransformedImage([0, 0]);96 return {97 x: Math.ceil(Math.max(start[0], this.visibleArea.x0)),98 y: Math.ceil(Math.max(start[1], this.visibleArea.y0))99 };100 }101 // Tells that the bitmap (this.bitmapImage) has been modified, and the internal caches flushed102 notifyBitmapImageChanged() {103 this.cacheBitmapDirty = true;104 }105 onCopy() {106 if (this.tool === 'rect' && this.rectStart) {107 const result = { rect: this.getSelectionRectangle() };108 this.clearSelection();109 return result;110 } else if (this.tool === 'select') {111 const selected = this.getSelectedIndicator();112 if (selected) {113 return {114 indicator: selected,115 rect: makeRectangleWH(selected.x, selected.y, selected.w, selected.h)116 };117 }118 }119 return null;120 }121 onKeyDown(e) {122 if (e.key === 'Enter' && this.pastedImage) {123 this.bakePastedImage();124 } else if (e.key === 'Escape' && this.pastedImage) {125 this.cancelPaste();126 } else if (e.key === 'Z' && !e.metaKey && !e.ctrlKey) {127 this.resetZoom();128 } else if (e.key === 'y' && !e.metaKey && !e.ctrlKey) {129 this.zoomAt(this.lastMousePos, 0.9);130 } else if (e.key === 'z' && !e.metaKey && !e.ctrlKey) {131 this.zoomAt(this.lastMousePos, 1.1);132 } else if (e.key === 'f' && !e.metaKey && !e.ctrlKey) {133 const selected = this.bitmapImage.indicators.find(i => i.highlighted);134 if (selected) this.focusArea(selected.x, selected.y, selected.w, selected.h, false, true);135 }136 else return false;137 return true;138 }139 onChangeState(goingForward) {140 if (!goingForward) this.cancelPaste();141 }142 // Image: {x, y, width, height, pixels}143 pasteImage(image) {144 if (image.width > this.visibleArea.width || image.height > this.visibleArea.height) {145 return alert(`Image too big to paste here (${image.width}x${image.height}, available ${this.visibleArea.width}x${this.visibleArea.height})`);146 }147 const indicator = image.indicator ? {...image.indicator} : null;148 this.pastedImage = { ...image, indicator };149 this.clearSelection();150 this.notifyBitmapImageChanged();151 }152 onRender(dt) {153 const { context, dpr } = this;154 const { width, height } = this.canvas;155 // Pattern background, scaled according to device pixel ratio156 context.fillStyle = '#d0d0d0';157 context.setTransform(dpr, 0, 0, dpr, 0, 0);158 context.fillRect(0, 0, width, height);159 if (!this.bitmapImage) return;160 this.renderCachedBitmap(context);161 const topLeft = transform([this.visibleArea.x0, this.visibleArea.y0], this.transform);162 const bottomRight = transform([this.visibleArea.x1, this.visibleArea.y1], this.transform);163 context.fillStyle = this.backgroundPattern;164 context.fillRect(topLeft[0], topLeft[1], bottomRight[0] - topLeft[0], bottomRight[1] - topLeft[1]);165 context.transform(this.transform[0], this.transform[1], this.transform[3], this.transform[4], this.transform[6], this.transform[7]);166 context.imageSmoothingEnabled = false;167 this.renderImageList.forEach(p => {168 context.globalAlpha = p.opacity;169 context.drawImage(p.bitmap.getCanvasForDrawing(), this.visibleArea.x0, this.visibleArea.y0,170 p.bitmap.width / this.pixelW, p.bitmap.height / this.pixelH);171 });172 if (['brush', 'place'].includes(this.tool) && this.brushBitmap.isDrawable()) {173 let pos = this.posInTransformedImage(this.lastMousePos);174 if (this.tool === 'brush') pos = floorPos(pos);175 context.globalAlpha = this.blink([0], [1], 1000)[0];176 context.drawImage(this.brushBitmap.getCanvasForDrawing(), pos[0], pos[1], this.brushBitmap.width / this.pixelW, this.brushBitmap.height / this.pixelH);177 // To have a fixed line width (not dependent on transform)178 const posStart = transform(pos, this.transform);179 const posEnd = transform([pos[0] + this.brushBitmap.width / this.pixelW, pos[1] + this.brushBitmap.height / this.pixelH], this.transform);180 context.setTransform(dpr, 0, 0, dpr, 0, 0);181 this.drawDashedRectangle(context, posStart[0], posStart[1], posEnd[0], posEnd[1], '#ff0');182 context.globalAlpha = 1;183 }184 // Draw indicators185 context.font = 'Arial 8px';186 context.setTransform(dpr, 0, 0, dpr, 0, 0);187 this.bitmapImage.indicators.forEach(i => this.drawIndicator(i));188 if (this.pastedImage && this.pastedImage.indicator) {189 this.pastedImage.indicator.x = this.pastedImage.x;190 this.pastedImage.indicator.y = this.pastedImage.y;191 this.pastedImage.indicator.highlighted = false;192 this.drawIndicator(this.pastedImage.indicator, true);193 }194 else {195 this.drawPastedImageRectangle(context);196 }197 this.drawSelectionRectangle(context);198 }199 resetVisibleArea() {200 this.setVisibleArea(0, 0, this.bitmapImage.width, this.bitmapImage.height);201 }202 resetZoom() {203 this.setVisibleArea(this.visibleArea.x0, this.visibleArea.y0, this.visibleArea.width, this.visibleArea.height);204 }205 // Bitmap image is created in api.js (width, height, image, getPixel) and has a `indicators` field206 // which is an array of (x, y, w, h, text, highlighted)207 setBitmapImage(bitmapImage) {208 const needsInitialization = !this.bitmapImage;209 this.bitmapImage = bitmapImage;210 this.pixelW = this.bitmapImage.pixelsPerPixelW || 1;211 this.pixelH = this.bitmapImage.pixelsPerPixelH || 1;212 if (needsInitialization) {213 this.cacheBitmap = new CanvasImageData(this.context, this.bitmapImage.width * this.pixelW, this.bitmapImage.height * this.pixelH);214 mat3.identity(this.transform);215 this.resetVisibleArea();216 }217 }218 setTool(tool) {219 if (this.tool === tool) return;220 this.tool = tool;221 this.clearSelection();222 }223 setVisibleArea(x, y, w, h, focusOnIt = true, preserveZoom = false) {224 if (preserveZoom) {225 translate(this.transform, [this.visibleArea.x0 - x, this.visibleArea.y0 - y]);226 }227 Object.assign(this.visibleArea, { x0: x, y0: y, x1: x + w, y1: y + h });228 if (focusOnIt) this.focusArea(x, y, w, h, this.panMode === 'scroll');229 else this.ensureTransformInVisibleArea();230 this.notifyBitmapImageChanged();231 }232 // ------------------------------ PRIVATE ---------------------------------233 bakePastedImage() {234 if (this.onbakepastedimage && !this.onbakepastedimage(this.pastedImage)) {235 this.cancelPaste();236 }237 }238 clearSelection() {239 this.rectStart = null;240 }241 getHighlighted() {242 return this.bitmapImage.indicators.filter(i => i.highlighted);243 }244 getHighlightedIndices() {245 const indices = [];246 this.bitmapImage.indicators.forEach((i, index) => {247 if (i.highlighted) indices.push(index);248 });249 return indices;250 }251 dehighlightAll() {252 this.bitmapImage.indicators.forEach(i => i.highlighted = false);253 }254 drawIndicator(indicator, pasted) {255 if (indicator.indType === 'object' && !indicator.highlighted) return;256 //if (indicator.x + indicator.w <= this.visibleArea.x0 || indicator.y + indicator.h <= this.visibleArea.y0 || indicator.x >= this.visibleArea.x1 || indicator.y >= this.visibleArea.y1) return;257 const { context } = this;258 const { width, height } = this.canvas;259 const indicatorPos = [indicator.x, indicator.y];260 if (indicator.indType === 'object') {261 indicatorPos[0] += this.visibleArea.x0;262 indicatorPos[1] += this.visibleArea.y0;263 }264 const start = this.posOnScreen([indicatorPos[0], indicatorPos[1]]);265 const end = this.posOnScreen([indicatorPos[0] + indicator.w, indicatorPos[1] + indicator.h]);266 const x0 = start[0], y0 = start[1], x1 = end[0], y1 = end[1];267 // Outside of screen268 if (x1 < 0 || y1 < 0 || x0 >= width || y0 >= height) return;269 const gradient = context.createLinearGradient(x0, y0, x0, y1);270 let draw = true;271 if (pasted) {272 const lineColor = this.blink([255, 255, 255, 1], [0, 128, 64, 1], 1000, FUNCTIONS.linear);273 gradient.addColorStop(0, makeCssColor(lineColor));274 gradient.addColorStop(1, makeCssColor(lineColor));275 context.lineDashOffset = this.blink([0], [7.999], 300, FUNCTIONS.sawtooth)[0];276 context.setLineDash([5, 3]);277 } else if (indicator.highlighted && !(this.pastedImage && this.pastedImage.indicator)) {278 const colorTop = this.blink([128, 128, 0, 1], [255, 255, 128, 1], 1000);279 const colorBottom = this.blink([255, 0, 0, 1], [255, 128, 0, 1], 1000);280 gradient.addColorStop(0, makeCssColor(colorTop));281 gradient.addColorStop(1, makeCssColor(colorBottom));282 context.lineDashOffset = this.blink([0], [7.999], 300, FUNCTIONS.sawtooth)[0];283 context.setLineDash([5, 3]);284 } else if (!indicator.focused) {285 gradient.addColorStop(0, '#def');286 gradient.addColorStop(1, '#08f');287 context.setLineDash([]);288 } else {289 draw = false;290 }291 if (draw) {292 context.strokeStyle = gradient;293 context.lineWidth = 2;294 context.fillStyle = gradient;295 context.strokeRect(x0, y0, x1 - x0, y1 - y0);296 context.setLineDash([]);297 if (indicator.text) {298 context.strokeStyle = '#000';299 context.strokeText(indicator.text, x0 + 2, y0 + 8);300 context.fillText(indicator.text, x0 + 2, y0 + 8);301 }302 }303 if (indicator.selected && indicator.tw && indicator.th) {304 context.strokeStyle = '#def';305 context.lineWidth = 2;306 context.beginPath();307 // In focused mode of sprites/alike, we will just draw the lines separating the sprites308 for (let y = 0; y + indicator.th <= indicator.h; y += indicator.th)309 for (let x = 0; x + indicator.tw <= indicator.w; x += indicator.tw) {310 const start = this.posOnScreen([indicator.x + x + indicator.tw, indicator.y + y]);311 const end = this.posOnScreen([indicator.x + x, indicator.y + y + indicator.th]);312 context.moveTo(start[0], start[1]);313 if (x + indicator.tw < indicator.w) context.lineTo(start[0], end[1]);314 context.moveTo(start[0], end[1]);315 if (y + indicator.th < indicator.h) context.lineTo(end[0], end[1]);316 }317 context.stroke();318 }319 }320 drawPastedImageRectangle(context) {321 if (!this.pastedImage) return;322 const lineColor = this.blink([255, 255, 255, 1], [0, 128, 64, 1], 1000, FUNCTIONS.linear);323 let [x0, y0] = this.posOnScreen([this.pastedImage.x, this.pastedImage.y]);324 let [x1, y1] = this.posOnScreen([this.pastedImage.x + this.pastedImage.width, this.pastedImage.y + this.pastedImage.height]);325 this.drawDashedRectangle(context, x0, y0, x1, y1, makeCssColor(lineColor));326 }327 drawSelectionRectangle(context) {328 if (this.rectStart) {329 const rect = this.getSelectionRectangle();330 let [x0, y0] = this.posOnScreen([rect.x0, rect.y0]);331 let [x1, y1] = this.posOnScreen([rect.x1, rect.y1]);332 this.drawDashedRectangle(context, x0, y0, x1, y1, '#fff', this.tool === 'cloner' ? 'rgba(0, 0, 255, 0.5)' : null);333 }334 }335 posInTransformedImageClamped(pos) {336 const [x, y] = this.posInTransformedImage(pos);337 return [338 Math.min(this.visibleArea.x1 - 1, Math.max(this.visibleArea.x0, x)),339 Math.min(this.visibleArea.y1 - 1, Math.max(this.visibleArea.y0, y))340 ];341 }342 ensureTransformInVisibleArea() {343 this.ensureTransformInArea(this.visibleArea);344 }345 ensureTransformInArea(visibleArea) {346 const sizeInPixels = subtract(347 this.posOnScreen([visibleArea.x1, visibleArea.y1]),348 this.posOnScreen([visibleArea.x0, visibleArea.y0]));349 const visible = [Math.min(sizeInPixels[0], this.width), Math.min(sizeInPixels[1], this.height)];350 const offset = [(this.width - visible[0]) / 2, (this.height - visible[1]) / 2];351 let posInImage = this.posInTransformedImage([this.width - offset[0], this.height - offset[1]]);352 if (posInImage[0] > visibleArea.x1) translate(this.transform, [posInImage[0] - visibleArea.x1, 0]);353 if (posInImage[1] > visibleArea.y1) translate(this.transform, [0, posInImage[1] - visibleArea.y1]);354 posInImage = this.posInTransformedImage([offset[0], offset[1]]);355 if (posInImage[0] < visibleArea.x0) translate(this.transform, [posInImage[0] - visibleArea.x0, 0]);356 if (posInImage[1] < visibleArea.y0) translate(this.transform, [0, posInImage[1] - visibleArea.y0]);357 }358 indicatorAtPosition(x, y) {359 return this.indicatorsAtPosition(x, y)[0];360 }361 indicatorsAtPosition(x, y) {362 return [...this.bitmapImage.indicators].reverse().filter(i =>363 x >= i.x && y >= i.y && x < i.x + i.w && y < i.y + i.h);364 }365 indicatorsInRect(rect) {366 return this.bitmapImage.indicators.filter(i =>367 i.x + i.w > rect.x0 && i.x < rect.x1 && i.y + i.h > rect.y0 && i.y < rect.y1);368 }369 inPastedImage(x, y) {370 return x >= this.pastedImage.x && y >= this.pastedImage.y && x < this.pastedImage.x + this.pastedImage.width && y < this.pastedImage.y + this.pastedImage.height;371 }372 inVisibleArea(x, y) {373 return x >= this.visibleArea.x0 && x < this.visibleArea.x1 && y >= this.visibleArea.y0 && y < this.visibleArea.y1;374 }375 isRectTool() { return ['rect', 'cloner'].includes(this.tool); }376 concatenateMoveToWriteBuffer(pos) {377 const prev = this.writePathBuffer.slice(this.writePathBuffer.length - 2);378 while (pos[0] !== prev[0] || pos[1] !== prev[1]) {379 // Interpolate move to avoid spots when moving too fast380 for (let i = 0; i < 2; i++) {381 if (pos[i] > prev[i]) prev[i]++;382 else if (pos[i] < prev[i]) prev[i]--;383 }384 this.writePathBuffer = this.writePathBuffer.concat([...prev]);385 this.ondrawpixel(this.cacheBitmap, prev[0] - this.visibleArea.x0, prev[1] - this.visibleArea.y0, this.onrequestpathcolor());386 }387 }388 onDoubleClick(e, mousePos) {389 if (this.tool === 'select') {390 const imagePosition = this.posInTransformedImage(mousePos);391 const indicator = this.indicatorAtPosition(imagePosition[0], imagePosition[1]);392 if (this.onedititem && indicator) this.onedititem(indicator);393 }394 }395 onMouseDown(e, mousePos) {396 // onMouseDown can be called again in case the tool changes immediately (right click, etc.)397 if (!this.isDown) {398 this.isDown = true;399 this.hasMoved = false;400 this.switchedToSecondaryTool = false;401 this.draggedPastedImagePos = this.draggingPastedImage = null;402 this.moveModeLastPos = null;403 }404 if (mouseEventShouldMove(e)) {405 this.moveLastPos = mousePos;406 e.preventDefault(); // middle click triggers a move tool on Windows407 } else if (this.pastedImage && e.button === 0) {408 const transformed = this.posInTransformedImage(mousePos);409 if (this.inPastedImage(transformed[0], transformed[1])) {410 this.draggingPastedImage = transformed;411 // Copy to avoid floating point in pastedImage.x/y412 this.draggedPastedImagePos = [this.pastedImage.x, this.pastedImage.y];413 } else {414 this.bakePastedImage();415 this.isDown = false;416 }417 } else if (this.tool === 'move') {418 const imagePosition = this.posInTransformedImage(mousePos);419 const indicator = this.indicatorAtPosition(imagePosition[0], imagePosition[1]);420 if (indicator) {421 if (e.ctrlKey) {422 this.hasMoved = false;423 indicator.highlighted = !indicator.highlighted;424 } else {425 // We need an extra move to start moving the selection if it's a new selection, not if it was already selected (this is to prevent moving accidentally when you just want to select the item, but when the item is already selected we want to allow precise movement)426 this.hasMoved = indicator.highlighted;427 if (!indicator.highlighted) this.dehighlightAll();428 indicator.highlighted = true;429 this.moveModeInitialPos = this.moveModeLastPos = imagePosition;430 }431 this.onmoveselect(this.getHighlightedIndices());432 } else {433 if (!e.ctrlKey) this.dehighlightAll();434 this.onmoveselect(this.getHighlightedIndices());435 this.rectStart = this.rectEnd = imagePosition;436 this.moveModeInitialPos = null;437 this.onMouseMove(e, mousePos);438 }439 } else if (this.isRectTool()) {440 this.rectStart = floorPos(this.posInTransformedImageClamped(mousePos));441 this.onMouseMove(e, mousePos);442 } else if (this.tool === 'place') {443 const pos = this.posInTransformedImage(mousePos);444 this.brushBitmap.isDrawable() && this.onplacetool(Math.round(pos[0] * this.pixelW), Math.round(pos[1] * this.pixelH));445 } else if (this.tool === 'brush') {446 if (e.button === 2 && this.onswitchtosecondarytool) {447 this.switchedToSecondaryTool = true;448 this.onswitchtosecondarytool(true);449 return this.onMouseDown(e, mousePos);450 }451 const pos = floorPos(this.posInTransformedImage(mousePos));452 this.brushBitmap.isDrawable() && this.onbrushpasted(pos[0], pos[1]);453 } else if (this.tool === 'eyedropper') {454 this.onMouseMove(e, mousePos);455 } else if (this.tool === 'pen') {456 const pos = this.posInTransformedImage(mousePos).map(p => Math.floor(p));457 if (!this.inVisibleArea(pos[0], pos[1])) return;458 if (e.button === 2 && this.onswitchtosecondarytool) {459 this.switchedToSecondaryTool = true;460 this.onswitchtosecondarytool(true);461 return this.onMouseDown(e, mousePos);462 }463 this.writePathBuffer = [...pos];464 this.ondrawpixel(this.cacheBitmap, pos[0] - this.visibleArea.x0, pos[1] - this.visibleArea.y0, this.onrequestpathcolor());465 } else if (this.tool === 'select') {466 const imagePosition = this.posInTransformedImage(mousePos);467 const imagePixelX = imagePosition[0] | 0, imagePixelY = imagePosition[1] | 0;468 if (!this.inVisibleArea(imagePixelX, imagePixelY)) {469 return this.onselectitem(null);470 }471 const indicators = this.indicatorsAtPosition(imagePosition[0], imagePosition[1]);472 if (indicators.length > 1) {473 showMultiSelectDialog(indicators, i => this.onselectitem(indicators[i]));474 } else {475 this.onselectitem(indicators[0]);476 }477 } else {478 const pos = floorPos(this.posInTransformedImageClamped(mousePos));479 this.onothertool && this.onothertool(this.tool, pos[0], pos[1]);480 }481 }482 onMouseMove(e, mousePos) {483 this.lastMousePos = mousePos;484 if (!this.isDown) return;485 if (this.draggingPastedImage) {486 const transformed = this.posInTransformedImage(mousePos);487 const dist = subtract(transformed, this.draggingPastedImage);488 this.draggedPastedImagePos = add(this.draggedPastedImagePos, dist);489 this.pastedImage.x = Math.round(this.draggedPastedImagePos[0]);490 this.pastedImage.y = Math.round(this.draggedPastedImagePos[1]);491 setStatusText(`Pasting at (${this.pastedImage.x}, ${this.pastedImage.y})`);492 this.draggingPastedImage = transformed;493 this.notifyBitmapImageChanged();494 } else if (this.moveLastPos) {495 const dist = subtract(mousePos, this.moveLastPos);496 // Require sensible movement else we consider it as a click497 if (!this.hasMoved && vec2.length(dist) < MIN_DISTANCE_FOR_MOVE) return;498 // Move view499 translate(this.transform, this.distanceInTransformedImage(dist));500 if (this.panMode === 'scroll') this.ensureTransformInVisibleArea();501 this.moveLastPos = mousePos;502 this.hasMoved = true;503 } else if (this.tool === 'move') {504 if (this.moveModeInitialPos) {505 const imagePosition = this.posInTransformedImage(mousePos);506 const dist = this.distanceOnScreen(subtract(imagePosition, this.moveModeLastPos));507 if (!this.hasMoved && vec2.length(dist) < MIN_DISTANCE_FOR_MOVE) return;508 this.getHighlighted().forEach(i => {509 i.x += imagePosition[0] - this.moveModeLastPos[0];510 i.y += imagePosition[1] - this.moveModeLastPos[1];511 });512 this.moveModeLastPos = imagePosition;513 this.hasMoved = true;514 } else {515 this.rectEnd = this.posInTransformedImage(mousePos);516 }517 } else if (this.isRectTool()) {518 this.rectEnd = floorPos(this.posInTransformedImageClamped(mousePos)).map(e => e + 1);519 if (this.tool === 'rect') {520 const sel = this.getSelectionRectangle();521 sel && setStatusText(`Rect size: ${sel.width}x${sel.height} (pos: ${sel.x0 - this.visibleArea.x0}, ${sel.y0 - this.visibleArea.y0})`);522 }523 } else if (this.tool === 'eyedropper') {524 const pos = this.posInTransformedImage(mousePos).map(p => Math.floor(p));525 this.oneyedropper && this.oneyedropper(pos[0], pos[1]);526 } else if (this.writePathBuffer) {527 // Pen mode528 const pos = this.posInTransformedImage(mousePos).map(p => Math.floor(p));529 if (!this.inVisibleArea(pos[0], pos[1])) return;530 this.concatenateMoveToWriteBuffer(pos);531 }532 }533 onMouseOut(e) {534 if (this.writePathBuffer) this.onpenwrite && this.onpenwrite(this.writePathBuffer);535 if (this.tool === 'move' && this.rectStart) {536 const indicators = this.indicatorsInRect(this.getSelectionRectangle());537 indicators.forEach(i => i.highlighted = true);538 this.onmoveselect(this.getHighlightedIndices());539 this.clearSelection();540 } else if (this.tool === 'cloner' && this.rectStart) {541 this.oncloner && this.oncloner(this.getSelectionRectangle());542 this.clearSelection();543 // Cloner always revert to brush tool544 this.onswitchtosecondarytool && this.onswitchtosecondarytool(false);545 }546 this.moveLastPos = this.isDown = this.writePathBuffer = null;547 this.hasMoved = false;548 if (this.switchedToSecondaryTool) this.onswitchtosecondarytool(false);549 }550 onMouseUp(e) {551 if (!this.isDown) return;552 if (this.tool === 'move' && this.hasMoved && this.moveModeLastPos) {553 const move = subtract(this.moveModeLastPos, this.moveModeInitialPos);554 this.onmovetool(this.getHighlightedIndices(), move[0] * this.pixelW, move[1] * this.pixelH);555 }...
DrawingInput.js
Source:DrawingInput.js
...138 const mouseData = useMouseSnapping(drawing, snappers, snappingDistance, applySnapping && !readOnly)139 const { snapper } = mouseData140 // Set up the selection rectangle.141 const isSelecting = !!processSelection && shouldBeSelecting(mouseDownData, startSelection) && mouseData && mouseData.position142 const selectionRectangle = isSelecting ? getSelectionRectangle(mouseDownData, mouseData, drawing) : undefined143 // Set up handler functions.144 const cancelDrag = useCallback(() => {145 setMouseDownData(undefined)146 }, [setMouseDownData])147 // Monitor the mouse going down and up.148 const startDrag = (evt) => {149 if (readOnly)150 return151 if (mouseDownData)152 return setMouseDownData(undefined) // Second touch! Cancel drawing to prevent confusion.153 const newMouseDownData = getMouseData(evt, snapper, drawing)154 const isSelecting = shouldBeSelecting(newMouseDownData, startSelection)155 if (!isSelecting && options.startDrag)156 options.startDrag(newMouseDownData)157 setMouseDownData(newMouseDownData)158 }159 const endDrag = (evt) => {160 if (readOnly || !mouseDownData)161 return162 const mouseUpData = getMouseData(evt, snapper, drawing)163 if (isSelecting && options.processSelection)164 options.processSelection(getSelectionRectangle(mouseDownData, mouseUpData, drawing), mouseUpData.utilKeys)165 if (!isSelecting && options.endDrag)166 options.endDrag(mouseDownData, mouseUpData)167 setMouseDownData(undefined)168 }169 useEventListener(['mousedown', 'touchstart'], startDrag, container, { passive: false })170 useEventListener(['mouseup', 'touchend'], endDrag)171 // Return all data.172 return { ...inputData, mouseData, mouseDownData, selectionRectangle, cancelDrag }173}174// The DrawingInput wrapper needs to be used to add the right classes and to properly position potential feedback.175export function DrawingInputUnforwarded({ Drawing, drawingProperties, className, inputData, options = {} }, drawingRef) {176 const drawingOptions = drawingProperties ? filterProperties(options, drawingProperties) : options177 options = processOptions(options, defaultDrawingInputOptions, true)178 let { maxWidth, stopSnapOnSelection, feedbackIconScale, onDelete } = options179 const { active, readOnly, mouseData, feedback, selectionRectangle } = inputData180 const drawing = drawingRef && drawingRef.current181 // Determine styling of the object.182 const classes = useStyles({183 maxWidth,184 active,185 readOnly,186 feedbackColor: feedback && feedback.color,187 feedbackType: feedback && feedback.type,188 hasFeedbackText: !!(feedback && feedback.text),189 })190 className = clsx(options.className, className, inputData.className, classes.DrawingInput, 'drawingInput', { active })191 // Add snap lines and a feedback icon.192 let { svgContents, htmlContents } = drawingOptions193 svgContents = addSelectionRectangle(svgContents, selectionRectangle, drawing)194 if (!selectionRectangle || !stopSnapOnSelection)195 svgContents = addSnapSvg(svgContents, mouseData, drawing)196 htmlContents = addFeedbackIcon(htmlContents, feedback, drawing, feedbackIconScale)197 // When an onDelete function is given, show a Garbage icon.198 if (onDelete) {199 htmlContents = <>200 {htmlContents}201 <PositionedElement anchor={[1, 1]} position={[drawing.width - 10, drawing.height - 10]} scale={1.5} ><DeleteButton onMouseDown={onDelete} onTouchStart={onDelete} /></PositionedElement>202 </>203 }204 // Show the drawing and the feedback box.205 return <div className={className}>206 <div className="drawing"><Drawing ref={drawingRef} {...{ ...drawingOptions, svgContents, htmlContents }} /></div>207 <div className="feedbackText">{feedback && feedback.text}</div>208 </div>209}210export const DrawingInput = forwardRef(DrawingInputUnforwarded)211export default DrawingInput212// useMouseSnapping wraps all the snapping functionalities into one hook. It takes a drawing, a set of snappers and a snapping distance and takes care of all the mouse functionalities.213function useMouseSnapping(drawing, snappers, snappingDistance, applySnapping) {214 // Process the current mouse position.215 const mousePosition = useMousePosition(drawing)216 const mouseInDrawing = drawing ? drawing.isInside(mousePosition) : false217 // Extract snapping lines and set up a snapper based on it.218 const snappingLines = useSnappingLines(snappers)219 const snapper = useCallback((point) => snapMousePosition(point, snappingLines, snappingDistance, applySnapping), [snappingLines, snappingDistance, applySnapping])220 const snapResult = snapper(mousePosition)221 // Return all data.222 return { mousePosition, mouseInDrawing, snappingLines, snapper, ...snapResult }223}224// useSnappingLines takes a snappers array and determines the snapping lines from it. It only recalculates on a change and filters duplicates.225function useSnappingLines(snappers) {226 snappers = useConsistentValue(snappers)227 return useMemo(() => {228 const snappingLines = []229 snappers.forEach(snapper => {230 if (snapper instanceof Line) {231 snappingLines.push(snapper)232 } else if (snapper instanceof Vector) {233 snappingLines.push(Line.getHorizontalThrough(snapper))234 snappingLines.push(Line.getVerticalThrough(snapper))235 } else if (snapper instanceof PositionedVector) {236 snappingLines.push(snapper.line)237 } else {238 throw new Error(`Invalid snapper: received a snapper with unexpected type. Make sure it is a vector, line or other allowed type.`)239 }240 })241 return filterDuplicates(snappingLines, (a, b) => a.equals(b))242 }, [snappers])243}244// snapMousePosition will calculate the position of the mouse after it's snapped to the nearest snapping line.245function snapMousePosition(position, snappingLines, snappingDistance, applySnapping) {246 // If there is no mouse position or no snapping should be applied, give a default response.247 if (!position || !applySnapping)248 return { position, snappedPosition: position, snapLines: [], isSnapped: false, isSnappedTwice: false }249 // Get all the lines that fall within snapping distance.250 const squaredSnappingDistance = snappingDistance ** 2251 const snappingLineSquaredDistances = snappingLines.map(line => line.getSquaredDistanceFrom(position)) // Calculate the squared distances.252 const selectedLines = numberArray(0, snappingLines.length - 1).filter(index => snappingLineSquaredDistances[index] <= squaredSnappingDistance) // Filter out all lines that are too far, and store the indices of the selected lines.253 let snapLines = sortByIndices(selectedLines.map(index => snappingLines[index]), selectedLines.map(index => snappingLineSquaredDistances[index])) // Sort by distance.254 // Depending on how many snap lines there are, snap the mouse position accordingly.255 let snappedPosition = position256 if (snapLines.length > 1) { // Multiple lines. Find the intersection and check that it's close enough to the mouse point.257 const intersection = snapLines[0].getIntersection(snapLines[1])258 if (intersection.squaredDistanceTo(position) <= squaredSnappingDistance) {259 snappedPosition = intersection260 snapLines = snapLines.filter(line => line.containsPoint(snappedPosition)) // Get rid of all snapping lines that don't go through this point.261 } else {262 snapLines = snapLines.slice(0, 1) // The snap position is too far from the mouse position. Only take the closest line and use that.263 }264 }265 if (snapLines.length === 1)266 snappedPosition = snapLines[0].getClosestPoint(position)267 const isSnapped = snapLines.length > 0268 const isSnappedTwice = snapLines.length > 1269 // Return the outcome.270 return { position, snappedPosition, snapLines, isSnapped, isSnappedTwice }271}272export function getMouseData(evt, snapper, drawing) {273 return { ...snapper(drawing.getPosition(getEventPosition(evt))), utilKeys: getUtilKeys(evt) }274}275// getSnapSvg takes a snapped mouse position and snap lines, and returns SVG to show the marker and the lines.276export function getSnapSvg(mouseData, drawing, lineStyle = {}, markerStyle = {}, snapMarkerSize = 6) {277 const { position, snappedPosition, snapLines } = mouseData278 const bounds = drawing && drawing.bounds279 // Don't show things when the mouse is outside the drawing.280 if (!drawing.isInside(position))281 return {}282 // Show the snap marker and lines.283 return {284 marker: snapLines.length > 0 ? <Square center={snappedPosition} side={snapMarkerSize} className="snapMarker" style={markerStyle} /> : null,285 lines: bounds ? snapLines.map((line, index) => {286 const linePart = bounds.getLinePart(line)287 return <SvgLine key={index} className="snapLine" points={[linePart.start, linePart.end]} style={lineStyle} />288 }) : [],289 }290}291// addSnapSvg takes SVG elements and adds snap lines to it.292export function addSnapSvg(svgContents, mouseData, drawing) {293 // If the drawing is not there yet, don't add lines.294 if (!drawing)295 return svgContents296 // Get the lines and marker and display them in the right order.297 const snapSvg = getSnapSvg(mouseData, drawing)298 return <>299 {snapSvg.lines}300 {svgContents}301 {snapSvg.marker}302 </>303}304// addFeedbackIcon takes HTML elements and adds a feedback icon to it.305export function addFeedbackIcon(htmlContents, feedback, drawing, scale = 1) {306 if (!feedback || !feedback.Icon)307 return htmlContents308 return <>309 {htmlContents}310 <PositionedElement anchor={[1, 0]} position={[drawing.width - 8, 6]} scale={scale} ><feedback.Icon className="icon" /></PositionedElement>311 </>312}313// shouldBeSelecting gets mouseDownData and startSelection options and determines if we're selecting.314function shouldBeSelecting(mouseDownData, startSelection) {315 // Don't start selecting if the mouse didn't go down.316 if (!mouseDownData)317 return false318 // Check the settings.319 switch (startSelection) {320 case startSelectionOptions.never:321 return false322 case startSelectionOptions.noDoubleSnap:323 return !mouseDownData.isSnappedTwice324 case startSelectionOptions.noSnap:325 return !mouseDownData.isSnapped326 case startSelectionOptions.always:327 return true328 default:329 throw new Error(`Invalid startSelection setting: received a setting of "${startSelection}" for startSelection on a DrawingInput, but this was not among the valid options.`)330 }331}332// getSelectionRectangle returns the selection rectangle based on two mouse data objects.333function getSelectionRectangle(downData, upData, drawing) {334 return new Rectangle({335 start: drawing.applyBounds(downData.position),336 end: drawing.applyBounds(upData.position),337 })338}339// addSelectionRectangle takes an svgContents object and adds a selection rectangle on top of it.340function addSelectionRectangle(svgContents, selectionRectangle) {341 return selectionRectangle ? <>342 {svgContents}343 <SvgRectangle className="selectionRectangle" dimensions={selectionRectangle} />344 </> : svgContents345}346// DeleteButton is a button of a garbage bin icon.347function DeleteButton(props) {...
edit.js
Source:edit.js
...345 ctx.stroke();346 ctx.setLineDash([]);347 }348 editSelection(uictx){349 const { left, right, top, bottom } = this.getSelectionRectangle(uictx);350 // update indices351 this.targets = [];352 const xform = this.curve.fullTransform; // .inverse()353 for(let i = 0; i < this.curve.length; ++i){354 const { x, y } = xform.applyTo(this.curve.getPoint(i));355 if(x >= left && x <= right356 && y >= top && y <= bottom){357 this.targets.push(i);358 }359 }360 }361 getSelectionRectangle(uictx){362 const sketchPos = uictx.getSketchPos();363 let left, top, right, bottom;364 if(this.sketchStart.x < sketchPos.x){365 left = this.sketchStart.x;366 right = sketchPos.x;367 } else {368 left = sketchPos.x;369 right = this.sketchStart.x;370 }371 if(this.sketchStart.y < sketchPos.y){372 top = this.sketchStart.y;373 bottom = sketchPos.y;374 } else {375 top = sketchPos.y;376 bottom = this.sketchStart.y;377 }378 return { left, right, top, bottom };379 }380 drawSelection(uictx){381 const ctx = uictx.getDrawingContext();382 ctx.setLineDash([2, 2]);383 ctx.strokeStyle = '#999999AA';384 const { left, right, top, bottom } = this.getSelectionRectangle(uictx);385 ctx.strokeRect(left, top, right - left, bottom - top);386 ctx.setLineDash([]);387 }388 stop(uictx){389 // update edit mode or the curve390 if(this.targets.length){391 if(this.editMode !== SELECT){392 // commit history393 uictx.commitHistory('edit ' + this.editMode);394 }395 // change target type to selection by default396 this.editMode = SELECT;397 398 } else {...
utils.js
Source:utils.js
...130 rect = getSelectionRectangleInContentEditableElement(element, position);131 else if (typeof element.createTextRange === 'function')132 rect = getTextSelectionRectangle(element, position);133 else134 rect = getSelectionRectangle(element, position);135 if (!rect)136 return null;137 rect = ensureRectangleInsideElement(element, rect);138 rect = getAbsoluteRect(rect);139 return {140 x: rect.left,141 y: Math.floor(rect.top + (rect.bottom - rect.top) / 2)142 };143}144export function getSelectionCoordinatesByPosition (element, position) {145 const isTextEditable = domUtils.isTextEditableElement(element);146 const isContentEditable = domUtils.isContentEditableElement(element);147 const hasText = isTextEditable && domUtils.getElementValue(element).length > 0 ||148 isContentEditable && contentEditable.getContentEditableValue(element).length;...
tile-selector-component.js
Source:tile-selector-component.js
...99 for (let x = 0; x < tilesWide; x++, tileNo++)100 drawTile32(this.cacheBitmap, x * tileset.tw, y * tileset.th, palette, tileset, tileNo);101 this.cacheBitmapDirty = false;102 }103 getSelectionRectangle() {104 if (!this.rectStart) return null;105 let [x0, y0] = this.rectStart, [x1, y1] = this.rectEnd;106 return makeSelectionRectangle(x0, y0, x1, y1);107 }108 drawSelectionRectangle(context) {109 if (this.rectStart) {110 const rect = this.getSelectionRectangle();111 const lineColor = this.blink([255, 255, 128, 1], [255, 0, 0, 1], 1000, FUNCTIONS.linear);112 let [x0, y0] = this.posOnScreen([rect.x0, rect.y0]);113 let [x1, y1] = this.posOnScreen([rect.x1, rect.y1]);114 const tileset = spriteNamed(this.tilesetName);115 this.drawDashedRectangle(context, x0 * tileset.tw, y0 * tileset.th, x1 * tileset.tw, y1 * tileset.th, makeCssColor(lineColor));116 }117 }118 getSingleTileSelectedIfAny() {119 const rect = this.getSelectionRectangle();120 if (rect && rect.width === 1 && rect.height === 1) {121 return this.tileAtPosition(rect.x0, rect.y0);122 }123 return -1;124 }125 roundPosToTile(posVec2) {126 const tileset = spriteNamed(this.tilesetName);127 return [Math.floor(posVec2[0] / tileset.tw), Math.floor(posVec2[1] / tileset.th)];128 }129 positionForTile(tileNo) {130 const tileset = spriteNamed(this.tilesetName);131 const tilesWide = Math.min(Math.floor(this.width / tileset.tw), tilesWideInTileset(tileset));132 return { x: tileNo % tilesWide, y: Math.floor(tileNo / tilesWide) };133 }134 tileAtPosition(x, y) {135 const tileset = spriteNamed(this.tilesetName);136 const tilesWide = Math.min(Math.floor(this.width / tileset.tw), tilesWideInTileset(tileset));137 const tileNo = x + y * tilesWide;138 return (tileNo < tilesInTileset(tileset) && x < tilesWide) ? tileNo : -1;139 }140 updateUI() {141 if (!this.tilesetName) return;142 const tile = this.getSingleTileSelectedIfAny();143 const tilePal = this.element('.tile-pal'), tileNo = this.element('.tile-no');144 const tileset = spriteNamed(this.tilesetName);145 tileNo.value = tile;146 tileNo.max = tilesInTileset(tileset) - 1;147 tilePal.disabled = tileNo.disabled = tile < 0;148 tilePal.max = paletteNamed(this.paletteName).h - 1;149 tilePal.value = Math.min(tilePal.value, tilePal.max);150 }151 selectRect() {152 const rect = this.getSelectionRectangle();153 const paletteMask = this.getPaletteMask();154 if (rect) {155 const cells = new Array(rect.width * rect.height);156 let cell = 0;157 for (let j = rect.y0; j < rect.y1; j++)158 for (let i = rect.x0; i < rect.x1; i++, cell++)159 cells[cell] = this.tileAtPosition(i, j) | paletteMask;160 this.onselect(rect.width, rect.height, cells);161 }162 }163 // ------------------------------ OVERRIDE ---------------------------------164 get dpr() { return 1; }165 onMouseDown(e, mousePos) {166 const pos = this.roundPosToTile(this.posInTransformedImage(mousePos));...
snapshot.js
Source:snapshot.js
...51 $('#snapshot_container').popover('show');52 }, 500);53 };54 $scope.upload_webcam = function(){55 if (cropObj.getSelectionRectangle()) {56 // Display loading; will display regardless of success or failure57 // $('.loader').fadeIn();58 $('#snapshot_container').popover('hide');59 $scope.loading = true;60 $scope.cutDisabled = true;61 var name = $scope.pyuserid;62 var formData = {"name":name, "data":canvas.toDataURL('image/png'), "count": selfieCount};63 $http.post('/fileupload', formData).success(function(data){64 $scope.cut();65 }).error(function(){66 $scope.loading = false;67 $scope.cutDisabled = true;68 alert("There was an issue uploading the image.");69 });70 }71 };72 //Call grabcut with coordinates73 $scope.cut = function(){74 var coord = cropObj.getSelectionRectangle().getOpenCVXYWH();75 var formData = {};76 formData['coords'] = coord.x + ' ' + coord.y + ' ' + coord.width + ' ' + coord.height;77 formData['pyuserid'] = $scope.pyuserid;78 formData['count'] = selfieCount;79 $http.post('/grabcut', formData).success(function(data){80 if (!Date.now) {81 Date.now = function() { return new Date().getTime(); }82 }83 console.log(data)84 $scope.selfie = data + "?" + Date.now();85 $('.check_image').attr('src', data);86 $scope.check();87 })88 .error(function(){...
PopUpMenu.js
Source:PopUpMenu.js
...47 getNewHidestyle(){48 return { visibility: 'hidden' };49 }50 getNewShowStyle(){51 const selectionRect = getSelectionRectangle();52 const top = `${this.getMenuTopPosition(selectionRect)}px`;53 const left = `${this.getMenuLeftPosition(selectionRect)}px`;54 return {55 top,56 left,57 }58 }59 getMenuTopPosition(rect){60 const menuMargin = 15;61 return rect.top + getScrollAlongYAxis() - this.getMenuHeight() - menuMargin;62 }63 getMenuLeftPosition(rect){64 return rect.left + getScrollAlongXAxis() - this.getMenuWidht()/2 + rect.width/2;65 }...
Using AI Code Generation
1import { Selector } from 'testcafe';2test('My first test', async t => {3 .click('#populate')4 .click('#submit-button')5 .takeElementScreenshot(Selector('#submit-button'));6});7import { Selector } from 'testcafe';8test('My first test', async t => {9 .click('#populate')10 .click('#submit-button')11 .takeElementScreenshot(Selector('#submit-button'));12});
Using AI Code Generation
1import { Selector } from 'testcafe';2test('My first test', async t => {3 .click('#populate')4 .click('#submit-button')5 .takeElementScreenshot(Selector('#submit-button'));6});7test('My first test', async t => {8 .click('#populate')9 .click('#submit-button')10 .takeElementScreenshot(Selector('#submit-button'), { selector: Selector('#submit-button') });11});12test('My first test', async t => {13 .click('#populate')14 .click('#submit-button')15 .takeElementScreenshot(Selector('#submit-button'), { selector: Selector('#submit-button'), includeMargins: true });16});17test('My first test', async t => {18 .click('#populate')19 .click('#submit-button')20 .takeElementScreenshot(Selector('#submit-button'), { selector: Selector('#submit-button'), includeMargins: true, includeBorders: true });21});22test('My first test', async t => {23 .click('#populate')24 .click('#submit-button')25 .takeElementScreenshot(Selector('#submit-button'), { selector: Selector('#submit-button'), includeMargins: true, includeBorders: true, includePadding: true });26});27test('My first test', async t => {28 .click('#populate')29 .click('#submit-button')30 .takeElementScreenshot(Selector('#submit-button'), { selector: Selector('#submit-button'), includeMargins: true, includeBorders: true, includePadding: true, crop: { left: 10, top: 10, right: 10, bottom: 10 } });31});32test('My first test', async t => {33 .click('#populate')34 .click('#submit-button')35 .takeElementScreenshot(Selector('#submit-button'), { selector: Selector('#submit-button'),
Using AI Code Generation
1import { Selector } from 'testcafe';2test('My first test', async t => {3 .typeText('#developer-name', 'John Smith')4 .click('#windows')5 .click('#submit-button');6 const articleHeader = await Selector('.result-content').find('h1');7 await t.expect(articleHeader.innerText).eql('Thank you, John Smith!');8});9const { Selector } = require('testcafe');10test('My first test', async t => {11 .typeText('#developer-name', 'John Smith')12 .click('#windows')13 .click('#submit-button');14 const articleHeader = await Selector('.result-content').find('h1');15 await t.expect(articleHeader.innerText).eql('Thank you, John Smith!');16});
Using AI Code Generation
1import { Selector } from 'testcafe';2test('My first test', async t => {3 .click('#populate')4 .click('#submit-button')5 .takeScreenshot()6 .takeElementScreenshot(Selector('#submit-button'))7 .takeElementScreenshot(Selector('#submit-button'), {crop: {left: 1, right: 1, top: 1, bottom: 1}})8 .takeElementScreenshot(Selector('#submit-button'), {crop: {left: 1, right: 1, top: 1, bottom: 1}, includeMargins: true})9 .takeElementScreenshot(Selector('#submit-button'), {crop: {left: 1, right: 1, top: 1, bottom: 1}, includeBorders: true})10 .takeElementScreenshot(Selector('#submit-button'), {crop: {left: 1, right: 1, top: 1, bottom: 1}, includePaddings: true})11 .takeElementScreenshot(Selector('#submit-button'), {crop: {left: 1, right: 1, top: 1, bottom: 1}, includePaddings: true, includeBorders: true, includeMargins: true})12 .takeScreenshot({fullPage: true})13 .takeScreenshot({fullPage: true, path: 'screenshots/fullpage.png'})14 .takeScreenshot({path: 'screenshots/viewport.png'})15 .takeScreenshot({crop: {left: 1, right: 1, top: 1, bottom: 1}})16 .takeScreenshot({crop: {left: 1, right: 1, top: 1, bottom: 1}, path: 'screenshots/cropped.png'})17 .takeScreenshot({crop: {left: 1, right: 1, top: 1, bottom: 1}, fullPage: true})18 .takeScreenshot({crop: {left: 1, right: 1, top: 1, bottom: 1}, fullPage: true, path: 'screenshots/cropped-fullpage.png'})19 .takeScreenshot({path: 'screenshots/with-shadow-dom-element.png', selector: '#shadow-host'})20 .takeElementScreenshot(Selector
Using AI Code Generation
1import { Selector } from 'testcafe';2test('My first test', async t => {3 const developerNameInput = Selector('#developer-name');4 const inputRectangle = await t.getSelectionRectangle(developerNameInput);5 const remoteCheckbox = Selector('#remote-testing');6 const checkboxRectangle = await t.getSelectionRectangle(remoteCheckbox);7 const windowsOption = Selector('option').withText('Windows');8 const optionRectangle = await t.getSelectionRectangle(windowsOption);9});
Using AI Code Generation
1import { Selector } from 'testcafe';2test('Getting element\'s rectangle', async t => {3 const searchBox = Selector('#lst-ib');4 .typeText(searchBox, 'testcafe')5 .click(searchBox)6 .pressKey('enter')7 .click(Selector('h3').withText('TestCafe - Automated browser testing'))8 .takeElementScreenshot(await searchBox.getBoundingRect(), 'search-box.png');9});10import { t } from 'testcafe';11export default class TestCafe {12 constructor () {13 this.searchBox = Selector('#lst-ib');14 }15 async search (searchText) {16 .typeText(this.searchBox, searchText)17 .click(this.searchBox)18 .pressKey('enter');19 }20 async clickOnSearchResult (searchResult) {21 await t.click(Selector('h3').withText(searchResult));22 }23}24import { Selector } from 'testcafe';25import TestCafe from './testcafe';26test('Getting element\'s rectangle', async t => {27 const testCafe = new TestCafe();28 await testCafe.search('testcafe');29 await testCafe.clickOnSearchResult('TestCafe - Automated browser testing');30 await t.takeElementScreenshot(await testCafe.searchBox.getBoundingRect(), 'search-box.png');31});32import { t } from 'testcafe';33export default class TestCafe {34 constructor () {35 this.searchBox = Selector('#lst-ib');36 }37 async search (searchText) {38 .typeText(this.searchBox, searchText)39 .click(this.searchBox)40 .pressKey('enter');41 }42 async clickOnSearchResult (searchResult) {43 await t.click(Selector('h3').withText(searchResult));44 }45}
Using AI Code Generation
1import {Selector} from 'testcafe';2test('Getting selection rectangle', async t => {3 await t.selectText(Selector('input[name="q"]')).pressKey('ctrl+c');4 const selectionRectangle = await t.getSelectionRectangle();5 console.log(selectionRectangle);6});7import {Selector} from 'testcafe';8test('Getting selection rectangle', async t => {9 await t.selectText(Selector('input[name="q"]')).pressKey('ctrl+c');10 const selectionRectangle = await t.getSelectionRectangle();11 console.log(selectionRectangle);12});13import {Selector} from 'testcafe';14test('Getting selection rectangle', async t => {15 await t.selectText(Selector('input[name="q"]')).pressKey('ctrl+c');16 const selectionRectangle = await t.getSelectionRectangle();17 console.log(selectionRectangle);18});19import {Selector} from 'testcafe';20test('Getting selection rectangle', async t => {21 await t.selectText(Selector('input[name="q"]')).pressKey('ctrl+c');22 const selectionRectangle = await t.getSelectionRectangle();23 console.log(selectionRectangle);24});25import {Selector} from 'testcafe';26test('Getting selection rectangle', async t => {27 await t.selectText(Selector('input[name="q"]')).pressKey('ctrl+c');28 const selectionRectangle = await t.getSelectionRectangle();
Using AI Code Generation
1import { ClientFunction } from 'testcafe';2const getSelectionRectangle = ClientFunction(() => {3 const selection = window.getSelection();4 const range = selection.getRangeAt(0);5 const rect = range.getBoundingClientRect();6 return {7 };8});9test('Getting Selection Rectangle', async t => {10 .selectText('#text')11 .expect(getSelectionRectangle()).eql({ left: 8, top: 8, width: 29, height: 16 });12});13import { ClientFunction } from 'testcafe';14const getSelectionRectangle = ClientFunction(() => {15 const selection = window.getSelection();16 const range = selection.getRangeAt(0);17 const rect = range.getBoundingClientRect();18 return {19 };20});21test('Getting Selection Rectangle', async t => {22 .selectText('#text')23 .expect(getSelectionRectangle()).eql({ left: 8, top: 8, width: 29, height: 16 });24});25import { ClientFunction } from 'testcafe';26const getSelectionRectangle = ClientFunction(() => {27 const selection = window.getSelection();28 const range = selection.getRangeAt(0);29 const rect = range.getBoundingClientRect();30 return {31 };32});33test('Getting Selection Rectangle', async t => {34 .selectText('#text')35 .expect(getSelectionRectangle()).eql({ left: 8, top: 8,
Using AI Code Generation
1import { Selector } from "testcafe";2test("Getting the Selected Text", async t => {3 .selectText(Selector("input[name='q']"))4 .expect(Selector("input[name='q']").getSelectionRectangle()).eql({ left: 0, top: 0, width: 0, height: 0 });5});6import { Selector } from "testcafe";7test("Getting the Selected Text", async t => {8 .selectText(Selector("input[name='q']"))9 .expect(Selector("input[name='q']").getSelectionRectangle()).eql({ left: 0, top: 0, width: 0, height: 0 });10});
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!