* update dependencies * fix #1008 by adjusting font-size (allowing to keep the text keep his proportions to the font) * refactor math.js
This commit is contained in:
@@ -124,7 +124,7 @@ export const remapElement = (selected, changes, m) => {
|
||||
case 'image': {
|
||||
// Allow images to be inverted (give them matrix when flipped)
|
||||
if (elName === 'image' && (m.a < 0 || m.d < 0)) {
|
||||
// Convert to matrix
|
||||
// Convert to matrix if flipped
|
||||
const chlist = getTransformList(selected)
|
||||
const mt = svgCanvas.getSvgRoot().createSVGTransform()
|
||||
mt.setMatrix(matrixMultiply(transformListToTransform(chlist).matrix, m))
|
||||
@@ -177,6 +177,19 @@ export const remapElement = (selected, changes, m) => {
|
||||
const pt = remap(changes.x, changes.y)
|
||||
changes.x = pt.x
|
||||
changes.y = pt.y
|
||||
|
||||
// Scale font-size
|
||||
let fontSize = selected.getAttribute('font-size')
|
||||
if (!fontSize) {
|
||||
// If not directly set, try computed style
|
||||
fontSize = window.getComputedStyle(selected).fontSize
|
||||
}
|
||||
const fontSizeNum = parseFloat(fontSize)
|
||||
if (!isNaN(fontSizeNum)) {
|
||||
// Assume uniform scaling and use m.a
|
||||
changes['font-size'] = fontSizeNum * Math.abs(m.a)
|
||||
}
|
||||
|
||||
finishUp()
|
||||
|
||||
// Handle child 'tspan' elements
|
||||
@@ -197,7 +210,17 @@ export const remapElement = (selected, changes, m) => {
|
||||
const childPtY = remap(changes.x, childY).y
|
||||
childChanges.y = childPtY
|
||||
}
|
||||
if (hasX || hasY) {
|
||||
|
||||
let tspanFS = child.getAttribute('font-size')
|
||||
if (!tspanFS) {
|
||||
tspanFS = window.getComputedStyle(child).fontSize
|
||||
}
|
||||
const tspanFSNum = parseFloat(tspanFS)
|
||||
if (!isNaN(tspanFSNum)) {
|
||||
childChanges['font-size'] = tspanFSNum * Math.abs(m.a)
|
||||
}
|
||||
|
||||
if (hasX || hasY || childChanges['font-size']) {
|
||||
assignAttributes(child, childChanges, 1000, true)
|
||||
}
|
||||
}
|
||||
@@ -208,6 +231,17 @@ export const remapElement = (selected, changes, m) => {
|
||||
const pt = remap(changes.x, changes.y)
|
||||
changes.x = pt.x
|
||||
changes.y = pt.y
|
||||
|
||||
// Handle tspan font-size scaling
|
||||
let tspanFS = selected.getAttribute('font-size')
|
||||
if (!tspanFS) {
|
||||
tspanFS = window.getComputedStyle(selected).fontSize
|
||||
}
|
||||
const tspanFSNum = parseFloat(tspanFS)
|
||||
if (!isNaN(tspanFSNum)) {
|
||||
changes['font-size'] = tspanFSNum * Math.abs(m.a)
|
||||
}
|
||||
|
||||
finishUp()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -570,6 +570,7 @@ const mouseOutEvent = () => {
|
||||
* @returns {void}
|
||||
*/
|
||||
const mouseUpEvent = (evt) => {
|
||||
evt.preventDefault()
|
||||
moveSelectionThresholdReached = false
|
||||
if (evt.button === 2) { return }
|
||||
if (!svgCanvas.getStarted()) { return }
|
||||
|
||||
@@ -3,117 +3,111 @@
|
||||
* @module math
|
||||
* @license MIT
|
||||
*
|
||||
* @copyright 2010 Alexis Deveria, 2010 Jeff Schiller
|
||||
* ©2010 Alexis Deveria, ©2010 Jeff Schiller
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {PlainObject} module:math.AngleCoord45
|
||||
* @property {Float} x - The angle-snapped x value
|
||||
* @property {Float} y - The angle-snapped y value
|
||||
* @property {Integer} a - The angle at which to snap
|
||||
* @typedef {Object} AngleCoord45
|
||||
* @property {number} x - The angle-snapped x value
|
||||
* @property {number} y - The angle-snapped y value
|
||||
* @property {number} a - The angle (in radians) at which to snap
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {PlainObject} module:math.XYObject
|
||||
* @property {Float} x
|
||||
* @property {Float} y
|
||||
* @typedef {Object} XYObject
|
||||
* @property {number} x
|
||||
* @property {number} y
|
||||
*/
|
||||
|
||||
import { NS } from './namespaces.js'
|
||||
|
||||
// Constants
|
||||
const NEAR_ZERO = 1e-14
|
||||
const NEAR_ZERO = 1e-10
|
||||
|
||||
// Throw away SVGSVGElement used for creating matrices/transforms.
|
||||
// Create a throwaway SVG element for matrix operations
|
||||
const svg = document.createElementNS(NS.SVG, 'svg')
|
||||
|
||||
/**
|
||||
* A (hopefully) quicker function to transform a point by a matrix
|
||||
* (this function avoids any DOM calls and just does the math).
|
||||
* @function module:math.transformPoint
|
||||
* @param {Float} x - Float representing the x coordinate
|
||||
* @param {Float} y - Float representing the y coordinate
|
||||
* @param {SVGMatrix} m - Matrix object to transform the point with
|
||||
* @returns {module:math.XYObject} An x, y object representing the transformed point
|
||||
* Transforms a point by a given matrix without DOM calls.
|
||||
* @function transformPoint
|
||||
* @param {number} x - The x coordinate
|
||||
* @param {number} y - The y coordinate
|
||||
* @param {SVGMatrix} m - The transformation matrix
|
||||
* @returns {XYObject} The transformed point
|
||||
*/
|
||||
export const transformPoint = function (x, y, m) {
|
||||
return { x: m.a * x + m.c * y + m.e, y: m.b * x + m.d * y + m.f }
|
||||
}
|
||||
export const transformPoint = (x, y, m) => ({
|
||||
x: m.a * x + m.c * y + m.e,
|
||||
y: m.b * x + m.d * y + m.f
|
||||
})
|
||||
|
||||
export const getTransformList = (elem) => {
|
||||
if (elem.transform) {
|
||||
return elem.transform?.baseVal
|
||||
/**
|
||||
* Gets the transform list (baseVal) from an element if it exists.
|
||||
* @function getTransformList
|
||||
* @param {Element} elem - An SVG element or element with a transform list
|
||||
* @returns {SVGTransformList|undefined} The transform list, if any
|
||||
*/
|
||||
export const getTransformList = elem => {
|
||||
if (elem.transform?.baseVal) {
|
||||
return elem.transform.baseVal
|
||||
}
|
||||
if (elem.gradientTransform) {
|
||||
return elem.gradientTransform?.baseVal
|
||||
if (elem.gradientTransform?.baseVal) {
|
||||
return elem.gradientTransform.baseVal
|
||||
}
|
||||
if (elem.patternTransform) {
|
||||
return elem.patternTransform?.baseVal
|
||||
if (elem.patternTransform?.baseVal) {
|
||||
return elem.patternTransform.baseVal
|
||||
}
|
||||
console.warn('no transform list found - check browser version', elem)
|
||||
console.warn('No transform list found. Check browser compatibility.', elem)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to check if the matrix performs no actual transform
|
||||
* (i.e. exists for identity purposes).
|
||||
* @function module:math.isIdentity
|
||||
* @param {SVGMatrix} m - The matrix object to check
|
||||
* @returns {boolean} Indicates whether or not the matrix is 1,0,0,1,0,0
|
||||
* Checks if a matrix is the identity matrix.
|
||||
* @function isIdentity
|
||||
* @param {SVGMatrix} m - The matrix to check
|
||||
* @returns {boolean} True if it's an identity matrix (1,0,0,1,0,0)
|
||||
*/
|
||||
export const isIdentity = function (m) {
|
||||
return (
|
||||
m.a === 1 && m.b === 0 && m.c === 0 && m.d === 1 && m.e === 0 && m.f === 0
|
||||
)
|
||||
}
|
||||
export const isIdentity = m =>
|
||||
m.a === 1 && m.b === 0 && m.c === 0 && m.d === 1 && m.e === 0 && m.f === 0
|
||||
|
||||
/**
|
||||
* This function tries to return a `SVGMatrix` that is the multiplication `m1 * m2`.
|
||||
* We also round to zero when it's near zero.
|
||||
* @function module:math.matrixMultiply
|
||||
* @param {...SVGMatrix} args - Matrix objects to multiply
|
||||
* @returns {SVGMatrix} The matrix object resulting from the calculation
|
||||
* Multiplies multiple matrices together (m1 * m2 * ...).
|
||||
* Near-zero values are rounded to zero.
|
||||
* @function matrixMultiply
|
||||
* @param {...SVGMatrix} args - The matrices to multiply
|
||||
* @returns {SVGMatrix} The resulting matrix
|
||||
*/
|
||||
export const matrixMultiply = function (...args) {
|
||||
const m = args.reduceRight((prev, m1) => {
|
||||
return m1.multiply(prev)
|
||||
})
|
||||
export const matrixMultiply = (...args) => {
|
||||
// If no matrices are given, return an identity matrix
|
||||
if (args.length === 0) {
|
||||
return svg.createSVGMatrix()
|
||||
}
|
||||
|
||||
if (Math.abs(m.a) < NEAR_ZERO) {
|
||||
m.a = 0
|
||||
}
|
||||
if (Math.abs(m.b) < NEAR_ZERO) {
|
||||
m.b = 0
|
||||
}
|
||||
if (Math.abs(m.c) < NEAR_ZERO) {
|
||||
m.c = 0
|
||||
}
|
||||
if (Math.abs(m.d) < NEAR_ZERO) {
|
||||
m.d = 0
|
||||
}
|
||||
if (Math.abs(m.e) < NEAR_ZERO) {
|
||||
m.e = 0
|
||||
}
|
||||
if (Math.abs(m.f) < NEAR_ZERO) {
|
||||
m.f = 0
|
||||
}
|
||||
const m = args.reduceRight((prev, curr) => curr.multiply(prev))
|
||||
|
||||
// Round near-zero values to zero
|
||||
if (Math.abs(m.a) < NEAR_ZERO) m.a = 0
|
||||
if (Math.abs(m.b) < NEAR_ZERO) m.b = 0
|
||||
if (Math.abs(m.c) < NEAR_ZERO) m.c = 0
|
||||
if (Math.abs(m.d) < NEAR_ZERO) m.d = 0
|
||||
if (Math.abs(m.e) < NEAR_ZERO) m.e = 0
|
||||
if (Math.abs(m.f) < NEAR_ZERO) m.f = 0
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
/**
|
||||
* See if the given transformlist includes a non-indentity matrix transform.
|
||||
* @function module:math.hasMatrixTransform
|
||||
* @param {SVGTransformList} [tlist] - The transformlist to check
|
||||
* @returns {boolean} Whether or not a matrix transform was found
|
||||
* Checks if a transform list includes a non-identity matrix transform.
|
||||
* @function hasMatrixTransform
|
||||
* @param {SVGTransformList} [tlist] - The transform list to check
|
||||
* @returns {boolean} True if a matrix transform is found
|
||||
*/
|
||||
export const hasMatrixTransform = function (tlist) {
|
||||
if (!tlist) {
|
||||
return false
|
||||
}
|
||||
let num = tlist.numberOfItems
|
||||
while (num--) {
|
||||
const xform = tlist.getItem(num)
|
||||
if (xform.type === 1 && !isIdentity(xform.matrix)) {
|
||||
export const hasMatrixTransform = tlist => {
|
||||
if (!tlist) return false
|
||||
for (let i = 0; i < tlist.numberOfItems; i++) {
|
||||
const xform = tlist.getItem(i)
|
||||
if (
|
||||
xform.type === SVGTransform.SVG_TRANSFORM_MATRIX &&
|
||||
!isIdentity(xform.matrix)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -121,29 +115,29 @@ export const hasMatrixTransform = function (tlist) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {PlainObject} module:math.TransformedBox An object with the following values
|
||||
* @property {module:math.XYObject} tl - The top left coordinate
|
||||
* @property {module:math.XYObject} tr - The top right coordinate
|
||||
* @property {module:math.XYObject} bl - The bottom left coordinate
|
||||
* @property {module:math.XYObject} br - The bottom right coordinate
|
||||
* @property {PlainObject} aabox - Object with the following values:
|
||||
* @property {Float} aabox.x - Float with the axis-aligned x coordinate
|
||||
* @property {Float} aabox.y - Float with the axis-aligned y coordinate
|
||||
* @property {Float} aabox.width - Float with the axis-aligned width coordinate
|
||||
* @property {Float} aabox.height - Float with the axis-aligned height coordinate
|
||||
* @typedef {Object} TransformedBox
|
||||
* @property {XYObject} tl - Top-left coordinate
|
||||
* @property {XYObject} tr - Top-right coordinate
|
||||
* @property {XYObject} bl - Bottom-left coordinate
|
||||
* @property {XYObject} br - Bottom-right coordinate
|
||||
* @property {Object} aabox
|
||||
* @property {number} aabox.x - Axis-aligned x
|
||||
* @property {number} aabox.y - Axis-aligned y
|
||||
* @property {number} aabox.width - Axis-aligned width
|
||||
* @property {number} aabox.height - Axis-aligned height
|
||||
*/
|
||||
|
||||
/**
|
||||
* Transforms a rectangle based on the given matrix.
|
||||
* @function module:math.transformBox
|
||||
* @param {Float} l - Float with the box's left coordinate
|
||||
* @param {Float} t - Float with the box's top coordinate
|
||||
* @param {Float} w - Float with the box width
|
||||
* @param {Float} h - Float with the box height
|
||||
* @param {SVGMatrix} m - Matrix object to transform the box by
|
||||
* @returns {module:math.TransformedBox}
|
||||
* Transforms a rectangular box using a given matrix.
|
||||
* @function transformBox
|
||||
* @param {number} l - Left coordinate
|
||||
* @param {number} t - Top coordinate
|
||||
* @param {number} w - Width
|
||||
* @param {number} h - Height
|
||||
* @param {SVGMatrix} m - Transformation matrix
|
||||
* @returns {TransformedBox} The transformed box information
|
||||
*/
|
||||
export const transformBox = function (l, t, w, h, m) {
|
||||
export const transformBox = (l, t, w, h, m) => {
|
||||
const tl = transformPoint(l, t, m)
|
||||
const tr = transformPoint(l + w, t, m)
|
||||
const bl = transformPoint(l, t + h, m)
|
||||
@@ -169,91 +163,81 @@ export const transformBox = function (l, t, w, h, m) {
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns a single matrix Transform for a given Transform List
|
||||
* (this is the equivalent of `SVGTransformList.consolidate()` but unlike
|
||||
* that method, this one does not modify the actual `SVGTransformList`).
|
||||
* This function is very liberal with its `min`, `max` arguments.
|
||||
* @function module:math.transformListToTransform
|
||||
* @param {SVGTransformList} tlist - The transformlist object
|
||||
* @param {Integer} [min=0] - Optional integer indicating start transform position
|
||||
* @param {Integer} [max] - Optional integer indicating end transform position;
|
||||
* defaults to one less than the tlist's `numberOfItems`
|
||||
* @returns {SVGTransform} A single matrix transform object
|
||||
* Consolidates a transform list into a single matrix transform without modifying the original list.
|
||||
* @function transformListToTransform
|
||||
* @param {SVGTransformList} tlist - The transform list
|
||||
* @param {number} [min=0] - Optional start index
|
||||
* @param {number} [max] - Optional end index, defaults to tlist length-1
|
||||
* @returns {SVGTransform} A single transform from the combined matrices
|
||||
*/
|
||||
export const transformListToTransform = function (tlist, min, max) {
|
||||
export const transformListToTransform = (tlist, min = 0, max = null) => {
|
||||
if (!tlist) {
|
||||
// Or should tlist = null have been prevented before this?
|
||||
return svg.createSVGTransformFromMatrix(svg.createSVGMatrix())
|
||||
}
|
||||
min = min || 0
|
||||
max = max || tlist.numberOfItems - 1
|
||||
min = Number.parseInt(min)
|
||||
max = Number.parseInt(max)
|
||||
if (min > max) {
|
||||
const temp = max
|
||||
max = min
|
||||
min = temp
|
||||
}
|
||||
let m = svg.createSVGMatrix()
|
||||
for (let i = min; i <= max; ++i) {
|
||||
// if our indices are out of range, just use a harmless identity matrix
|
||||
const mtom =
|
||||
|
||||
const start = Number.parseInt(min, 10)
|
||||
const end = Number.parseInt(max ?? tlist.numberOfItems - 1, 10)
|
||||
const low = Math.min(start, end)
|
||||
const high = Math.max(start, end)
|
||||
|
||||
let combinedMatrix = svg.createSVGMatrix()
|
||||
for (let i = low; i <= high; i++) {
|
||||
// If out of range, use identity
|
||||
const currentMatrix =
|
||||
i >= 0 && i < tlist.numberOfItems
|
||||
? tlist.getItem(i).matrix
|
||||
: svg.createSVGMatrix()
|
||||
m = matrixMultiply(m, mtom)
|
||||
combinedMatrix = matrixMultiply(combinedMatrix, currentMatrix)
|
||||
}
|
||||
return svg.createSVGTransformFromMatrix(m)
|
||||
|
||||
return svg.createSVGTransformFromMatrix(combinedMatrix)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the matrix object for a given element.
|
||||
* @function module:math.getMatrix
|
||||
* @param {Element} elem - The DOM element to check
|
||||
* @returns {SVGMatrix} The matrix object associated with the element's transformlist
|
||||
* Gets the matrix of a given element's transform list.
|
||||
* @function getMatrix
|
||||
* @param {Element} elem - The element to check
|
||||
* @returns {SVGMatrix} The transformation matrix
|
||||
*/
|
||||
export const getMatrix = (elem) => {
|
||||
export const getMatrix = elem => {
|
||||
const tlist = getTransformList(elem)
|
||||
return transformListToTransform(tlist).matrix
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a 45 degree angle coordinate associated with the two given
|
||||
* coordinates.
|
||||
* @function module:math.snapToAngle
|
||||
* @param {Integer} x1 - First coordinate's x value
|
||||
* @param {Integer} y1 - First coordinate's y value
|
||||
* @param {Integer} x2 - Second coordinate's x value
|
||||
* @param {Integer} y2 - Second coordinate's y value
|
||||
* @returns {module:math.AngleCoord45}
|
||||
* Returns a coordinate snapped to the nearest 45-degree angle.
|
||||
* @function snapToAngle
|
||||
* @param {number} x1 - First point's x
|
||||
* @param {number} y1 - First point's y
|
||||
* @param {number} x2 - Second point's x
|
||||
* @param {number} y2 - Second point's y
|
||||
* @returns {AngleCoord45} The angle-snapped coordinates and angle
|
||||
*/
|
||||
export const snapToAngle = (x1, y1, x2, y2) => {
|
||||
const snap = Math.PI / 4 // 45 degrees
|
||||
const dx = x2 - x1
|
||||
const dy = y2 - y1
|
||||
const angle = Math.atan2(dy, dx)
|
||||
const dist = Math.sqrt(dx * dx + dy * dy)
|
||||
const snapangle = Math.round(angle / snap) * snap
|
||||
const dist = Math.hypot(dx, dy)
|
||||
const snapAngle = Math.round(angle / snap) * snap
|
||||
|
||||
return {
|
||||
x: x1 + dist * Math.cos(snapangle),
|
||||
y: y1 + dist * Math.sin(snapangle),
|
||||
a: snapangle
|
||||
x: x1 + dist * Math.cos(snapAngle),
|
||||
y: y1 + dist * Math.sin(snapAngle),
|
||||
a: snapAngle
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if two rectangles (BBoxes objects) intersect each other.
|
||||
* @function module:math.rectsIntersect
|
||||
* @param {SVGRect} r1 - The first BBox-like object
|
||||
* @param {SVGRect} r2 - The second BBox-like object
|
||||
* @returns {boolean} True if rectangles intersect
|
||||
* Checks if two rectangles intersect.
|
||||
* Both r1 and r2 are expected to have {x, y, width, height}.
|
||||
* @function rectsIntersect
|
||||
* @param {{x:number,y:number,width:number,height:number}} r1 - First rectangle
|
||||
* @param {{x:number,y:number,width:number,height:number}} r2 - Second rectangle
|
||||
* @returns {boolean} True if the rectangles intersect
|
||||
*/
|
||||
export const rectsIntersect = (r1, r2) => {
|
||||
return (
|
||||
r2.x < r1.x + r1.width &&
|
||||
r2.x + r2.width > r1.x &&
|
||||
r2.y < r1.y + r1.height &&
|
||||
r2.y + r2.height > r1.y
|
||||
)
|
||||
}
|
||||
export const rectsIntersect = (r1, r2) =>
|
||||
r2.x < r1.x + r1.width &&
|
||||
r2.x + r2.width > r1.x &&
|
||||
r2.y < r1.y + r1.height &&
|
||||
r2.y + r2.height > r1.y
|
||||
|
||||
@@ -6,33 +6,31 @@
|
||||
*/
|
||||
|
||||
import { NS } from './namespaces.js'
|
||||
import { transformPoint, getMatrix } from './math.js'
|
||||
import {
|
||||
transformPoint, getMatrix
|
||||
} from './math.js'
|
||||
import {
|
||||
assignAttributes, getElement, getBBox as utilsGetBBox
|
||||
assignAttributes,
|
||||
getElement,
|
||||
getBBox as utilsGetBBox
|
||||
} from './utilities.js'
|
||||
import {
|
||||
supportsGoodTextCharPos
|
||||
} from '../common/browser.js'
|
||||
import { supportsGoodTextCharPos } from '../common/browser.js'
|
||||
|
||||
let svgCanvas = null
|
||||
|
||||
/**
|
||||
* @function module:text-actions.init
|
||||
* @param {module:text-actions.svgCanvas} textActionsContext
|
||||
* @returns {void}
|
||||
*/
|
||||
export const init = (canvas) => {
|
||||
* @function module:text-actions.init
|
||||
* @param {module:text-actions.svgCanvas} textActionsContext
|
||||
* @returns {void}
|
||||
*/
|
||||
export const init = canvas => {
|
||||
svgCanvas = canvas
|
||||
}
|
||||
|
||||
/**
|
||||
* Group: Text edit functions
|
||||
* Functions relating to editing text elements.
|
||||
* @namespace {PlainObject} textActions
|
||||
* @memberof module:svgcanvas.SvgCanvas#
|
||||
*/
|
||||
* Group: Text edit functions
|
||||
* Functions relating to editing text elements.
|
||||
* @namespace {PlainObject} textActions
|
||||
* @memberof module:svgcanvas.SvgCanvas#
|
||||
*/
|
||||
export const textActionsMethod = (function () {
|
||||
let curtext
|
||||
let textinput
|
||||
@@ -42,23 +40,26 @@ export const textActionsMethod = (function () {
|
||||
let chardata = []
|
||||
let textbb // , transbb;
|
||||
let matrix
|
||||
let lastX; let lastY
|
||||
let lastX
|
||||
let lastY
|
||||
let allowDbl
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Integer} index
|
||||
* @returns {void}
|
||||
*/
|
||||
*
|
||||
* @param {Integer} index
|
||||
* @returns {void}
|
||||
*/
|
||||
function setCursor (index) {
|
||||
const empty = (textinput.value === '')
|
||||
const empty = textinput.value === ''
|
||||
textinput.focus()
|
||||
|
||||
if (!arguments.length) {
|
||||
if (empty) {
|
||||
index = 0
|
||||
} else {
|
||||
if (textinput.selectionEnd !== textinput.selectionStart) { return }
|
||||
if (textinput.selectionEnd !== textinput.selectionStart) {
|
||||
return
|
||||
}
|
||||
index = textinput.selectionEnd
|
||||
}
|
||||
}
|
||||
@@ -80,13 +81,13 @@ export const textActionsMethod = (function () {
|
||||
|
||||
if (!blinker) {
|
||||
blinker = setInterval(function () {
|
||||
const show = (cursor.getAttribute('display') === 'none')
|
||||
const show = cursor.getAttribute('display') === 'none'
|
||||
cursor.setAttribute('display', show ? 'inline' : 'none')
|
||||
}, 600)
|
||||
}
|
||||
|
||||
const startPt = ptToScreen(charbb.x, textbb.y)
|
||||
const endPt = ptToScreen(charbb.x, (textbb.y + textbb.height))
|
||||
const endPt = ptToScreen(charbb.x, textbb.y + textbb.height)
|
||||
|
||||
assignAttributes(cursor, {
|
||||
x1: startPt.x,
|
||||
@@ -97,16 +98,18 @@ export const textActionsMethod = (function () {
|
||||
display: 'inline'
|
||||
})
|
||||
|
||||
if (selblock) { selblock.setAttribute('d', '') }
|
||||
if (selblock) {
|
||||
selblock.setAttribute('d', '')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Integer} start
|
||||
* @param {Integer} end
|
||||
* @param {boolean} skipInput
|
||||
* @returns {void}
|
||||
*/
|
||||
*
|
||||
* @param {Integer} start
|
||||
* @param {Integer} end
|
||||
* @param {boolean} skipInput
|
||||
* @returns {void}
|
||||
*/
|
||||
function setSelection (start, end, skipInput) {
|
||||
if (start === end) {
|
||||
setCursor(end)
|
||||
@@ -137,12 +140,29 @@ export const textActionsMethod = (function () {
|
||||
const tl = ptToScreen(startbb.x, textbb.y)
|
||||
const tr = ptToScreen(startbb.x + (endbb.x - startbb.x), textbb.y)
|
||||
const bl = ptToScreen(startbb.x, textbb.y + textbb.height)
|
||||
const br = ptToScreen(startbb.x + (endbb.x - startbb.x), textbb.y + textbb.height)
|
||||
const br = ptToScreen(
|
||||
startbb.x + (endbb.x - startbb.x),
|
||||
textbb.y + textbb.height
|
||||
)
|
||||
|
||||
const dstr = 'M' + tl.x + ',' + tl.y +
|
||||
' L' + tr.x + ',' + tr.y +
|
||||
' ' + br.x + ',' + br.y +
|
||||
' ' + bl.x + ',' + bl.y + 'z'
|
||||
const dstr =
|
||||
'M' +
|
||||
tl.x +
|
||||
',' +
|
||||
tl.y +
|
||||
' L' +
|
||||
tr.x +
|
||||
',' +
|
||||
tr.y +
|
||||
' ' +
|
||||
br.x +
|
||||
',' +
|
||||
br.y +
|
||||
' ' +
|
||||
bl.x +
|
||||
',' +
|
||||
bl.y +
|
||||
'z'
|
||||
|
||||
assignAttributes(selblock, {
|
||||
d: dstr,
|
||||
@@ -151,11 +171,11 @@ export const textActionsMethod = (function () {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Float} mouseX
|
||||
* @param {Float} mouseY
|
||||
* @returns {Integer}
|
||||
*/
|
||||
*
|
||||
* @param {Float} mouseX
|
||||
* @param {Float} mouseY
|
||||
* @returns {Integer}
|
||||
*/
|
||||
function getIndexFromPoint (mouseX, mouseY) {
|
||||
// Position cursor here
|
||||
const pt = svgCanvas.getSvgRoot().createSVGPoint()
|
||||
@@ -163,7 +183,9 @@ export const textActionsMethod = (function () {
|
||||
pt.y = mouseY
|
||||
|
||||
// No content, so return 0
|
||||
if (chardata.length === 1) { return 0 }
|
||||
if (chardata.length === 1) {
|
||||
return 0
|
||||
}
|
||||
// Determine if cursor should be on left or right of character
|
||||
let charpos = curtext.getCharNumAtPosition(pt)
|
||||
if (charpos < 0) {
|
||||
@@ -176,7 +198,7 @@ export const textActionsMethod = (function () {
|
||||
charpos = chardata.length - 2
|
||||
}
|
||||
const charbb = chardata[charpos]
|
||||
const mid = charbb.x + (charbb.width / 2)
|
||||
const mid = charbb.x + charbb.width / 2
|
||||
if (mouseX > mid) {
|
||||
charpos++
|
||||
}
|
||||
@@ -184,22 +206,22 @@ export const textActionsMethod = (function () {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Float} mouseX
|
||||
* @param {Float} mouseY
|
||||
* @returns {void}
|
||||
*/
|
||||
*
|
||||
* @param {Float} mouseX
|
||||
* @param {Float} mouseY
|
||||
* @returns {void}
|
||||
*/
|
||||
function setCursorFromPoint (mouseX, mouseY) {
|
||||
setCursor(getIndexFromPoint(mouseX, mouseY))
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Float} x
|
||||
* @param {Float} y
|
||||
* @param {boolean} apply
|
||||
* @returns {void}
|
||||
*/
|
||||
*
|
||||
* @param {Float} x
|
||||
* @param {Float} y
|
||||
* @param {boolean} apply
|
||||
* @returns {void}
|
||||
*/
|
||||
function setEndSelectionFromPoint (x, y, apply) {
|
||||
const i1 = textinput.selectionStart
|
||||
const i2 = getIndexFromPoint(x, y)
|
||||
@@ -210,11 +232,11 @@ export const textActionsMethod = (function () {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Float} xIn
|
||||
* @param {Float} yIn
|
||||
* @returns {module:math.XYObject}
|
||||
*/
|
||||
*
|
||||
* @param {Float} xIn
|
||||
* @param {Float} yIn
|
||||
* @returns {module:math.XYObject}
|
||||
*/
|
||||
function screenToPt (xIn, yIn) {
|
||||
const out = {
|
||||
x: xIn,
|
||||
@@ -234,11 +256,11 @@ export const textActionsMethod = (function () {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Float} xIn
|
||||
* @param {Float} yIn
|
||||
* @returns {module:math.XYObject}
|
||||
*/
|
||||
*
|
||||
* @param {Float} xIn
|
||||
* @param {Float} yIn
|
||||
* @returns {module:math.XYObject}
|
||||
*/
|
||||
function ptToScreen (xIn, yIn) {
|
||||
const out = {
|
||||
x: xIn,
|
||||
@@ -258,22 +280,24 @@ export const textActionsMethod = (function () {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Event} evt
|
||||
* @returns {void}
|
||||
*/
|
||||
*
|
||||
* @param {Event} evt
|
||||
* @returns {void}
|
||||
*/
|
||||
function selectAll (evt) {
|
||||
setSelection(0, curtext.textContent.length)
|
||||
evt.target.removeEventListener('click', selectAll)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Event} evt
|
||||
* @returns {void}
|
||||
*/
|
||||
*
|
||||
* @param {Event} evt
|
||||
* @returns {void}
|
||||
*/
|
||||
function selectWord (evt) {
|
||||
if (!allowDbl || !curtext) { return }
|
||||
if (!allowDbl || !curtext) {
|
||||
return
|
||||
}
|
||||
const zoom = svgCanvas.getZoom()
|
||||
const ept = transformPoint(evt.pageX, evt.pageY, svgCanvas.getrootSctm())
|
||||
const mouseX = ept.x * zoom
|
||||
@@ -297,30 +321,30 @@ export const textActionsMethod = (function () {
|
||||
|
||||
return /** @lends module:svgcanvas.SvgCanvas#textActions */ {
|
||||
/**
|
||||
* @param {Element} target
|
||||
* @param {Float} x
|
||||
* @param {Float} y
|
||||
* @returns {void}
|
||||
*/
|
||||
* @param {Element} target
|
||||
* @param {Float} x
|
||||
* @param {Float} y
|
||||
* @returns {void}
|
||||
*/
|
||||
select (target, x, y) {
|
||||
curtext = target
|
||||
svgCanvas.textActions.toEditMode(x, y)
|
||||
},
|
||||
/**
|
||||
* @param {Element} elem
|
||||
* @returns {void}
|
||||
*/
|
||||
* @param {Element} elem
|
||||
* @returns {void}
|
||||
*/
|
||||
start (elem) {
|
||||
curtext = elem
|
||||
svgCanvas.textActions.toEditMode()
|
||||
},
|
||||
/**
|
||||
* @param {external:MouseEvent} evt
|
||||
* @param {Element} mouseTarget
|
||||
* @param {Float} startX
|
||||
* @param {Float} startY
|
||||
* @returns {void}
|
||||
*/
|
||||
* @param {external:MouseEvent} evt
|
||||
* @param {Element} mouseTarget
|
||||
* @param {Float} startX
|
||||
* @param {Float} startY
|
||||
* @returns {void}
|
||||
*/
|
||||
mouseDown (evt, mouseTarget, startX, startY) {
|
||||
const pt = screenToPt(startX, startY)
|
||||
|
||||
@@ -332,20 +356,20 @@ export const textActionsMethod = (function () {
|
||||
// TODO: Find way to block native selection
|
||||
},
|
||||
/**
|
||||
* @param {Float} mouseX
|
||||
* @param {Float} mouseY
|
||||
* @returns {void}
|
||||
*/
|
||||
* @param {Float} mouseX
|
||||
* @param {Float} mouseY
|
||||
* @returns {void}
|
||||
*/
|
||||
mouseMove (mouseX, mouseY) {
|
||||
const pt = screenToPt(mouseX, mouseY)
|
||||
setEndSelectionFromPoint(pt.x, pt.y)
|
||||
},
|
||||
/**
|
||||
* @param {external:MouseEvent} evt
|
||||
* @param {Float} mouseX
|
||||
* @param {Float} mouseY
|
||||
* @returns {void}
|
||||
*/
|
||||
* @param {external:MouseEvent} evt
|
||||
* @param {Float} mouseX
|
||||
* @param {Float} mouseY
|
||||
* @returns {void}
|
||||
*/
|
||||
mouseUp (evt, mouseX, mouseY) {
|
||||
const pt = screenToPt(mouseX, mouseY)
|
||||
|
||||
@@ -359,25 +383,25 @@ export const textActionsMethod = (function () {
|
||||
|
||||
if (
|
||||
evt.target !== curtext &&
|
||||
mouseX < lastX + 2 &&
|
||||
mouseX > lastX - 2 &&
|
||||
mouseY < lastY + 2 &&
|
||||
mouseY > lastY - 2
|
||||
mouseX < lastX + 2 &&
|
||||
mouseX > lastX - 2 &&
|
||||
mouseY < lastY + 2 &&
|
||||
mouseY > lastY - 2
|
||||
) {
|
||||
svgCanvas.textActions.toSelectMode(true)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @function
|
||||
* @param {Integer} index
|
||||
* @returns {void}
|
||||
*/
|
||||
* @function
|
||||
* @param {Integer} index
|
||||
* @returns {void}
|
||||
*/
|
||||
setCursor,
|
||||
/**
|
||||
* @param {Float} x
|
||||
* @param {Float} y
|
||||
* @returns {void}
|
||||
*/
|
||||
* @param {Float} x
|
||||
* @param {Float} y
|
||||
* @returns {void}
|
||||
*/
|
||||
toEditMode (x, y) {
|
||||
allowDbl = false
|
||||
svgCanvas.setCurrentMode('textedit')
|
||||
@@ -407,16 +431,20 @@ export const textActionsMethod = (function () {
|
||||
}, 300)
|
||||
},
|
||||
/**
|
||||
* @param {boolean|Element} selectElem
|
||||
* @fires module:svgcanvas.SvgCanvas#event:selected
|
||||
* @returns {void}
|
||||
*/
|
||||
* @param {boolean|Element} selectElem
|
||||
* @fires module:svgcanvas.SvgCanvas#event:selected
|
||||
* @returns {void}
|
||||
*/
|
||||
toSelectMode (selectElem) {
|
||||
svgCanvas.setCurrentMode('select')
|
||||
clearInterval(blinker)
|
||||
blinker = null
|
||||
if (selblock) { selblock.setAttribute('display', 'none') }
|
||||
if (cursor) { cursor.setAttribute('visibility', 'hidden') }
|
||||
if (selblock) {
|
||||
selblock.setAttribute('display', 'none')
|
||||
}
|
||||
if (cursor) {
|
||||
cursor.setAttribute('visibility', 'hidden')
|
||||
}
|
||||
curtext.style.cursor = 'move'
|
||||
|
||||
if (selectElem) {
|
||||
@@ -440,27 +468,30 @@ export const textActionsMethod = (function () {
|
||||
// }
|
||||
},
|
||||
/**
|
||||
* @param {Element} elem
|
||||
* @returns {void}
|
||||
*/
|
||||
* @param {Element} elem
|
||||
* @returns {void}
|
||||
*/
|
||||
setInputElem (elem) {
|
||||
textinput = elem
|
||||
},
|
||||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
* @returns {void}
|
||||
*/
|
||||
clear () {
|
||||
if (svgCanvas.getCurrentMode() === 'textedit') {
|
||||
svgCanvas.textActions.toSelectMode()
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @param {Element} _inputElem Not in use
|
||||
* @returns {void}
|
||||
*/
|
||||
* @param {Element} _inputElem Not in use
|
||||
* @returns {void}
|
||||
*/
|
||||
init (_inputElem) {
|
||||
if (!curtext) { return }
|
||||
let i; let end
|
||||
if (!curtext) {
|
||||
return
|
||||
}
|
||||
let i
|
||||
let end
|
||||
// if (supportsEditableText()) {
|
||||
// curtext.select();
|
||||
// return;
|
||||
@@ -490,7 +521,7 @@ export const textActionsMethod = (function () {
|
||||
curtext.addEventListener('dblclick', selectWord)
|
||||
|
||||
if (!len) {
|
||||
end = { x: textbb.x + (textbb.width / 2), width: 0 }
|
||||
end = { x: textbb.x + textbb.width / 2, width: 0 }
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
@@ -527,4 +558,4 @@ export const textActionsMethod = (function () {
|
||||
setSelection(textinput.selectionStart, textinput.selectionEnd, true)
|
||||
}
|
||||
}
|
||||
}())
|
||||
})()
|
||||
|
||||
Reference in New Issue
Block a user