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(
|
||||
strokeWidth: number,
|
||||
export function getHighlightFreehandSettings({
|
||||
strokeWidth,
|
||||
showAsComplete,
|
||||
isPen,
|
||||
}: {
|
||||
strokeWidth: number
|
||||
showAsComplete: boolean
|
||||
): StrokeOptions {
|
||||
isPen: boolean
|
||||
}): StrokeOptions {
|
||||
return {
|
||||
size: 1 + strokeWidth,
|
||||
thinning: 0.1,
|
||||
streamline: 0.1, // 0.62 + ((1 + strokeWidth) / 8) * 0.06,
|
||||
streamline: 0.5,
|
||||
smoothing: 0.5,
|
||||
simulatePressure: true,
|
||||
easing: EASINGS.easeOutSine,
|
||||
simulatePressure: !isPen,
|
||||
easing: isPen ? PEN_EASING : EASINGS.easeOutSine,
|
||||
last: showAsComplete,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,12 @@
|
|||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import {
|
||||
Box2d,
|
||||
getStrokeOutlinePoints,
|
||||
getStrokePoints,
|
||||
linesIntersect,
|
||||
setStrokePointRadii,
|
||||
Vec2d,
|
||||
VecLike,
|
||||
} from '@tldraw/primitives'
|
||||
import { Box2d, getStrokePoints, linesIntersect, Vec2d, VecLike } from '@tldraw/primitives'
|
||||
import { TLDrawShapeSegment, TLHighlightShape } from '@tldraw/tlschema'
|
||||
import { last, rng } from '@tldraw/utils'
|
||||
import { SVGContainer } from '../../../components/SVGContainer'
|
||||
import { FONT_SIZES } from '../../../constants'
|
||||
import { getSvgPathFromStroke, getSvgPathFromStrokePoints } from '../../../utils/svg'
|
||||
import { getSvgPathFromStrokePoints } from '../../../utils/svg'
|
||||
import { getHighlightFreehandSettings, getPointsFromSegments } from '../DrawShapeUtil/getPath'
|
||||
import { ShapeUtil, TLOnResizeHandler } from '../ShapeUtil'
|
||||
import { ShapeFill } from '../shared/ShapeFill'
|
||||
import { TLExportColors } from '../shared/TLExportColors'
|
||||
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 options = getHighlightFreehandSettings(strokeWidth, showAsComplete)
|
||||
const options = getHighlightFreehandSettings({
|
||||
strokeWidth,
|
||||
showAsComplete,
|
||||
isPen: shape.props.isPen,
|
||||
})
|
||||
const strokePoints = getStrokePoints(allPointsFromSegments, options)
|
||||
|
||||
let strokePath
|
||||
|
@ -202,6 +197,29 @@ function getIndicatorDot(point: VecLike, sw: number) {
|
|||
},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({
|
||||
strokeWidth,
|
||||
shape,
|
||||
|
@ -212,51 +230,17 @@ function HighlightRenderer({
|
|||
opacity?: number
|
||||
}) {
|
||||
const forceSolid = useForceSolid()
|
||||
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(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>
|
||||
)
|
||||
}
|
||||
const { solidStrokePath, sw } = getHighlightSvgPath(shape, strokeWidth, forceSolid)
|
||||
|
||||
return (
|
||||
<SVGContainer id={shape.id} style={{ opacity }}>
|
||||
<ShapeFill fill="none" color={shape.props.color} d={solidStrokePath} />
|
||||
<path
|
||||
d={solidStrokePath}
|
||||
strokeLinecap="round"
|
||||
fill="none"
|
||||
pointerEvents="all"
|
||||
stroke={`var(--palette-${shape.props.color}-highlight)`}
|
||||
strokeWidth={strokeWidth}
|
||||
strokeDashoffset="0"
|
||||
strokeWidth={sw}
|
||||
/>
|
||||
</SVGContainer>
|
||||
)
|
||||
|
@ -268,28 +252,14 @@ function highlighterToSvg(
|
|||
opacity: number,
|
||||
colors: TLExportColors
|
||||
) {
|
||||
const { color } = shape.props
|
||||
|
||||
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 { solidStrokePath, sw } = getHighlightSvgPath(shape, strokeWidth, false)
|
||||
|
||||
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
|
||||
path.setAttribute('d', getSvgPathFromStroke(strokeOutlinePoints, true))
|
||||
path.setAttribute('fill', colors.highlight[color])
|
||||
path.setAttribute('stroke-linecap', 'round')
|
||||
path.setAttribute('opacity', opacity.toString())
|
||||
path.setAttribute('d', solidStrokePath)
|
||||
path.setAttribute('fill', 'none')
|
||||
path.setAttribute('stroke', colors.highlight[shape.props.color])
|
||||
path.setAttribute('stroke-width', `${sw}`)
|
||||
path.setAttribute('opacity', `${opacity}`)
|
||||
|
||||
return path
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ function CommonStylePickerSet({ props }: { props: TLNullableShapeProps }) {
|
|||
return null
|
||||
}
|
||||
|
||||
const showPickers = fill || dash || size
|
||||
const showPickers = fill !== undefined || dash !== undefined || size !== undefined
|
||||
|
||||
const opacityIndex = styles.opacity.findIndex((s) => s.id === opacity)
|
||||
|
||||
|
|
Loading…
Reference in a new issue