highlighter fixes (#1530)
Fixes the following issues with highlighter: * Exported highlighter has much larger stroke width than in-app highlighter * Selecting two highlighter shapes with different sizes would hide the size option from the styles panel * Highlighter lines drawn on ipad look noise-y ### Change Type <!-- 💡 Indicate the type of change your pull request is. --> <!-- 🤷♀️ If you're not sure, don't select anything --> <!-- ✂️ Feel free to delete unselected options --> <!-- To select one, put an x in the box: [x] --> - [x] `patch` — Bug Fix - [ ] `minor` — New Feature - [ ] `major` — Breaking Change - [ ] `dependencies` — Dependency Update (publishes a `patch` release, for devDependencies use `internal`) - [ ] `documentation` — Changes to the documentation only (will not publish a new version) - [ ] `tests` — Changes to any testing-related code only (will not publish a new version) - [ ] `internal` — Any other changes that don't affect the published package (will not publish a new version) ### Test Plan - ### Release Notes [aq bug fixes]
This commit is contained in:
parent
d646994554
commit
d9548556cb
3 changed files with 51 additions and 76 deletions
|
@ -36,17 +36,22 @@ const solidSettings = (strokeWidth: number): StrokeOptions => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getHighlightFreehandSettings(
|
export function getHighlightFreehandSettings({
|
||||||
strokeWidth: number,
|
strokeWidth,
|
||||||
|
showAsComplete,
|
||||||
|
isPen,
|
||||||
|
}: {
|
||||||
|
strokeWidth: number
|
||||||
showAsComplete: boolean
|
showAsComplete: boolean
|
||||||
): StrokeOptions {
|
isPen: boolean
|
||||||
|
}): StrokeOptions {
|
||||||
return {
|
return {
|
||||||
size: 1 + strokeWidth,
|
size: 1 + strokeWidth,
|
||||||
thinning: 0.1,
|
thinning: 0.1,
|
||||||
streamline: 0.1, // 0.62 + ((1 + strokeWidth) / 8) * 0.06,
|
streamline: 0.5,
|
||||||
smoothing: 0.5,
|
smoothing: 0.5,
|
||||||
simulatePressure: true,
|
simulatePressure: !isPen,
|
||||||
easing: EASINGS.easeOutSine,
|
easing: isPen ? PEN_EASING : EASINGS.easeOutSine,
|
||||||
last: showAsComplete,
|
last: showAsComplete,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,12 @@
|
||||||
/* eslint-disable react-hooks/rules-of-hooks */
|
/* eslint-disable react-hooks/rules-of-hooks */
|
||||||
import {
|
import { Box2d, getStrokePoints, linesIntersect, Vec2d, VecLike } from '@tldraw/primitives'
|
||||||
Box2d,
|
|
||||||
getStrokeOutlinePoints,
|
|
||||||
getStrokePoints,
|
|
||||||
linesIntersect,
|
|
||||||
setStrokePointRadii,
|
|
||||||
Vec2d,
|
|
||||||
VecLike,
|
|
||||||
} from '@tldraw/primitives'
|
|
||||||
import { TLDrawShapeSegment, TLHighlightShape } from '@tldraw/tlschema'
|
import { TLDrawShapeSegment, TLHighlightShape } from '@tldraw/tlschema'
|
||||||
import { last, rng } from '@tldraw/utils'
|
import { last, rng } from '@tldraw/utils'
|
||||||
import { SVGContainer } from '../../../components/SVGContainer'
|
import { SVGContainer } from '../../../components/SVGContainer'
|
||||||
import { FONT_SIZES } from '../../../constants'
|
import { FONT_SIZES } from '../../../constants'
|
||||||
import { getSvgPathFromStroke, getSvgPathFromStrokePoints } from '../../../utils/svg'
|
import { getSvgPathFromStrokePoints } from '../../../utils/svg'
|
||||||
import { getHighlightFreehandSettings, getPointsFromSegments } from '../DrawShapeUtil/getPath'
|
import { getHighlightFreehandSettings, getPointsFromSegments } from '../DrawShapeUtil/getPath'
|
||||||
import { ShapeUtil, TLOnResizeHandler } from '../ShapeUtil'
|
import { ShapeUtil, TLOnResizeHandler } from '../ShapeUtil'
|
||||||
import { ShapeFill } from '../shared/ShapeFill'
|
|
||||||
import { TLExportColors } from '../shared/TLExportColors'
|
import { TLExportColors } from '../shared/TLExportColors'
|
||||||
import { useForceSolid } from '../shared/useForceSolid'
|
import { useForceSolid } from '../shared/useForceSolid'
|
||||||
|
|
||||||
|
@ -133,7 +124,11 @@ export class HighlightShapeUtil extends ShapeUtil<TLHighlightShape> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const showAsComplete = shape.props.isComplete || last(shape.props.segments)?.type === 'straight'
|
const showAsComplete = shape.props.isComplete || last(shape.props.segments)?.type === 'straight'
|
||||||
const options = getHighlightFreehandSettings(strokeWidth, showAsComplete)
|
const options = getHighlightFreehandSettings({
|
||||||
|
strokeWidth,
|
||||||
|
showAsComplete,
|
||||||
|
isPen: shape.props.isPen,
|
||||||
|
})
|
||||||
const strokePoints = getStrokePoints(allPointsFromSegments, options)
|
const strokePoints = getStrokePoints(allPointsFromSegments, options)
|
||||||
|
|
||||||
let strokePath
|
let strokePath
|
||||||
|
@ -202,6 +197,29 @@ function getIndicatorDot(point: VecLike, sw: number) {
|
||||||
},0`
|
},0`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getHighlightSvgPath(shape: TLHighlightShape, strokeWidth: number, forceSolid: boolean) {
|
||||||
|
const allPointsFromSegments = getPointsFromSegments(shape.props.segments)
|
||||||
|
const showAsComplete = shape.props.isComplete || last(shape.props.segments)?.type === 'straight'
|
||||||
|
|
||||||
|
let sw = strokeWidth
|
||||||
|
if (!forceSolid && !shape.props.isPen && allPointsFromSegments.length === 1) {
|
||||||
|
sw += rng(shape.id)() * (strokeWidth / 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = getHighlightFreehandSettings({
|
||||||
|
strokeWidth: sw,
|
||||||
|
showAsComplete,
|
||||||
|
isPen: shape.props.isPen,
|
||||||
|
})
|
||||||
|
const strokePoints = getStrokePoints(allPointsFromSegments, options)
|
||||||
|
const solidStrokePath =
|
||||||
|
strokePoints.length > 1
|
||||||
|
? getSvgPathFromStrokePoints(strokePoints, false)
|
||||||
|
: getShapeDot(allPointsFromSegments[0])
|
||||||
|
|
||||||
|
return { solidStrokePath, sw }
|
||||||
|
}
|
||||||
|
|
||||||
function HighlightRenderer({
|
function HighlightRenderer({
|
||||||
strokeWidth,
|
strokeWidth,
|
||||||
shape,
|
shape,
|
||||||
|
@ -212,51 +230,17 @@ function HighlightRenderer({
|
||||||
opacity?: number
|
opacity?: number
|
||||||
}) {
|
}) {
|
||||||
const forceSolid = useForceSolid()
|
const forceSolid = useForceSolid()
|
||||||
const allPointsFromSegments = getPointsFromSegments(shape.props.segments)
|
const { solidStrokePath, sw } = getHighlightSvgPath(shape, strokeWidth, forceSolid)
|
||||||
|
|
||||||
const showAsComplete = shape.props.isComplete || last(shape.props.segments)?.type === 'straight'
|
|
||||||
|
|
||||||
let sw = strokeWidth
|
|
||||||
if (!forceSolid && !shape.props.isPen && allPointsFromSegments.length === 1) {
|
|
||||||
sw += rng(shape.id)() * (strokeWidth / 6)
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = getHighlightFreehandSettings(sw, showAsComplete)
|
|
||||||
|
|
||||||
const strokePoints = getStrokePoints(allPointsFromSegments, options)
|
|
||||||
|
|
||||||
const solidStrokePath =
|
|
||||||
strokePoints.length > 1
|
|
||||||
? getSvgPathFromStrokePoints(strokePoints, false)
|
|
||||||
: getShapeDot(allPointsFromSegments[0])
|
|
||||||
|
|
||||||
if (!forceSolid || strokePoints.length < 2) {
|
|
||||||
setStrokePointRadii(strokePoints, options)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SVGContainer id={shape.id} style={{ opacity }}>
|
|
||||||
<path
|
|
||||||
d={solidStrokePath}
|
|
||||||
strokeLinecap="round"
|
|
||||||
fill="none"
|
|
||||||
pointerEvents="all"
|
|
||||||
stroke={`var(--palette-${shape.props.color}-highlight)`}
|
|
||||||
strokeWidth={sw}
|
|
||||||
/>
|
|
||||||
</SVGContainer>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SVGContainer id={shape.id} style={{ opacity }}>
|
<SVGContainer id={shape.id} style={{ opacity }}>
|
||||||
<ShapeFill fill="none" color={shape.props.color} d={solidStrokePath} />
|
|
||||||
<path
|
<path
|
||||||
d={solidStrokePath}
|
d={solidStrokePath}
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
fill="none"
|
fill="none"
|
||||||
|
pointerEvents="all"
|
||||||
stroke={`var(--palette-${shape.props.color}-highlight)`}
|
stroke={`var(--palette-${shape.props.color}-highlight)`}
|
||||||
strokeWidth={strokeWidth}
|
strokeWidth={sw}
|
||||||
strokeDashoffset="0"
|
|
||||||
/>
|
/>
|
||||||
</SVGContainer>
|
</SVGContainer>
|
||||||
)
|
)
|
||||||
|
@ -268,28 +252,14 @@ function highlighterToSvg(
|
||||||
opacity: number,
|
opacity: number,
|
||||||
colors: TLExportColors
|
colors: TLExportColors
|
||||||
) {
|
) {
|
||||||
const { color } = shape.props
|
const { solidStrokePath, sw } = getHighlightSvgPath(shape, strokeWidth, false)
|
||||||
|
|
||||||
const allPointsFromSegments = getPointsFromSegments(shape.props.segments)
|
|
||||||
|
|
||||||
const showAsComplete = shape.props.isComplete || last(shape.props.segments)?.type === 'straight'
|
|
||||||
|
|
||||||
let sw = strokeWidth
|
|
||||||
if (!shape.props.isPen && allPointsFromSegments.length === 1) {
|
|
||||||
sw += rng(shape.id)() * (strokeWidth / 6)
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = getHighlightFreehandSettings(sw, showAsComplete)
|
|
||||||
const strokePoints = getStrokePoints(allPointsFromSegments, options)
|
|
||||||
|
|
||||||
setStrokePointRadii(strokePoints, options)
|
|
||||||
const strokeOutlinePoints = getStrokeOutlinePoints(strokePoints, options)
|
|
||||||
|
|
||||||
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
|
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
|
||||||
path.setAttribute('d', getSvgPathFromStroke(strokeOutlinePoints, true))
|
path.setAttribute('d', solidStrokePath)
|
||||||
path.setAttribute('fill', colors.highlight[color])
|
path.setAttribute('fill', 'none')
|
||||||
path.setAttribute('stroke-linecap', 'round')
|
path.setAttribute('stroke', colors.highlight[shape.props.color])
|
||||||
path.setAttribute('opacity', opacity.toString())
|
path.setAttribute('stroke-width', `${sw}`)
|
||||||
|
path.setAttribute('opacity', `${opacity}`)
|
||||||
|
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,7 +92,7 @@ function CommonStylePickerSet({ props }: { props: TLNullableShapeProps }) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const showPickers = fill || dash || size
|
const showPickers = fill !== undefined || dash !== undefined || size !== undefined
|
||||||
|
|
||||||
const opacityIndex = styles.opacity.findIndex((s) => s.id === opacity)
|
const opacityIndex = styles.opacity.findIndex((s) => s.id === opacity)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue