mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-06 20:11:38 +08:00
Bug: none Change-Id: I02ef1182143b7fe091ce92d59f04ac2188903e0d Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2886925 Commit-Queue: Jack Franklin <jacktfranklin@chromium.org> Auto-Submit: Jack Franklin <jacktfranklin@chromium.org> Reviewed-by: Tim van der Lippe <tvanderlippe@chromium.org> Reviewed-by: Mathias Bynens <mathias@chromium.org>
168 lines
5.5 KiB
TypeScript
168 lines
5.5 KiB
TypeScript
|
|
// Copyright 2021 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import type {Bounds, PathCommands, Position} from './common.js';
|
|
import type {LineStyle, PathBounds} from './highlight_common.js';
|
|
import {drawPath, emptyBounds} from './highlight_common.js';
|
|
|
|
type SnapAlignment = 'none'|'start'|'end'|'center';
|
|
export interface ScrollSnapHighlight {
|
|
snapport: PathCommands;
|
|
paddingBox: PathCommands;
|
|
snapAreas: Array<{
|
|
path: PathCommands,
|
|
borderBox: PathCommands,
|
|
alignBlock?: SnapAlignment,
|
|
alignInline?: SnapAlignment,
|
|
}>;
|
|
snapportBorder: LineStyle;
|
|
snapAreaBorder: LineStyle;
|
|
scrollMarginColor: string;
|
|
scrollPaddingColor: string;
|
|
}
|
|
|
|
function getSnapAlignBlockPoint(bounds: Bounds, align: SnapAlignment): Position|undefined {
|
|
if (align === 'start') {
|
|
return {
|
|
x: (bounds.minX + bounds.maxX) / 2,
|
|
y: bounds.minY,
|
|
};
|
|
}
|
|
if (align === 'center') {
|
|
return {
|
|
x: (bounds.minX + bounds.maxX) / 2,
|
|
y: (bounds.minY + bounds.maxY) / 2,
|
|
};
|
|
}
|
|
if (align === 'end') {
|
|
return {
|
|
x: (bounds.minX + bounds.maxX) / 2,
|
|
y: bounds.maxY,
|
|
};
|
|
}
|
|
return;
|
|
}
|
|
|
|
function getSnapAlignInlinePoint(bounds: Bounds, align: SnapAlignment): Position|undefined {
|
|
if (align === 'start') {
|
|
return {
|
|
x: bounds.minX,
|
|
y: (bounds.minY + bounds.maxY) / 2,
|
|
};
|
|
}
|
|
if (align === 'center') {
|
|
return {
|
|
x: (bounds.minX + bounds.maxX) / 2,
|
|
y: (bounds.minY + bounds.maxY) / 2,
|
|
};
|
|
}
|
|
if (align === 'end') {
|
|
return {
|
|
x: bounds.maxX,
|
|
y: (bounds.minY + bounds.maxY) / 2,
|
|
};
|
|
}
|
|
return;
|
|
}
|
|
|
|
const ALIGNMENT_POINT_STROKE_WIDTH = 5;
|
|
const ALIGNMENT_POINT_STROKE_COLOR = 'white';
|
|
const ALIGNMENT_POINT_OUTER_RADIUS = 6;
|
|
const ALIGNMENT_POINT_FILL_COLOR = '#4585f6';
|
|
const ALIGNMENT_POINT_INNER_RADIUS = 4;
|
|
|
|
function drawAlignment(context: CanvasRenderingContext2D, point: Position, bounds: Bounds): void {
|
|
let startAngle = 0;
|
|
let renderFullCircle = true;
|
|
if (point.x === bounds.minX) {
|
|
startAngle = -0.5 * Math.PI;
|
|
renderFullCircle = false;
|
|
} else if (point.x === bounds.maxX) {
|
|
startAngle = 0.5 * Math.PI;
|
|
renderFullCircle = false;
|
|
} else if (point.y === bounds.minY) {
|
|
startAngle = 0;
|
|
renderFullCircle = false;
|
|
} else if (point.y === bounds.maxY) {
|
|
startAngle = Math.PI;
|
|
renderFullCircle = false;
|
|
}
|
|
const endAngle = startAngle + (renderFullCircle ? 2 * Math.PI : Math.PI);
|
|
context.save();
|
|
context.beginPath();
|
|
context.lineWidth = ALIGNMENT_POINT_STROKE_WIDTH;
|
|
context.strokeStyle = ALIGNMENT_POINT_STROKE_COLOR;
|
|
context.arc(point.x, point.y, ALIGNMENT_POINT_OUTER_RADIUS, startAngle, endAngle);
|
|
context.stroke();
|
|
context.fillStyle = ALIGNMENT_POINT_FILL_COLOR;
|
|
context.arc(point.x, point.y, ALIGNMENT_POINT_INNER_RADIUS, startAngle, endAngle);
|
|
context.fill();
|
|
context.restore();
|
|
}
|
|
|
|
function drawScrollPadding(
|
|
highlight: ScrollSnapHighlight, context: CanvasRenderingContext2D, emulationScaleFactor: number) {
|
|
drawPath(
|
|
context, highlight.paddingBox, highlight.scrollPaddingColor, undefined, undefined, emptyBounds(),
|
|
emulationScaleFactor);
|
|
|
|
// Clear the area so that previously rendered paddings remain.
|
|
context.save();
|
|
context.globalCompositeOperation = 'destination-out';
|
|
drawPath(context, highlight.snapport, 'white', undefined, undefined, emptyBounds(), emulationScaleFactor);
|
|
context.restore();
|
|
}
|
|
|
|
function drawSnapAreas(
|
|
highlight: ScrollSnapHighlight, context: CanvasRenderingContext2D, emulationScaleFactor: number): PathBounds[] {
|
|
const bounds = [];
|
|
for (const area of highlight.snapAreas) {
|
|
const areaBounds = emptyBounds();
|
|
drawPath(
|
|
context, area.path, highlight.scrollMarginColor, highlight.snapAreaBorder.color,
|
|
highlight.snapAreaBorder.pattern, areaBounds, emulationScaleFactor);
|
|
|
|
// Clear the area so that previously rendered margins remain.
|
|
context.save();
|
|
context.globalCompositeOperation = 'destination-out';
|
|
drawPath(context, area.borderBox, 'white', undefined, undefined, emptyBounds(), emulationScaleFactor);
|
|
context.restore();
|
|
|
|
bounds.push(areaBounds);
|
|
}
|
|
return bounds;
|
|
}
|
|
|
|
function drawAlignmentPoints(
|
|
areaBounds: PathBounds[], highlight: ScrollSnapHighlight, context: CanvasRenderingContext2D) {
|
|
for (let i = 0; i < highlight.snapAreas.length; i++) {
|
|
const area = highlight.snapAreas[i];
|
|
const inlinePoint = area.alignInline ? getSnapAlignInlinePoint(areaBounds[i], area.alignInline) : null;
|
|
const blockPoint = area.alignBlock ? getSnapAlignBlockPoint(areaBounds[i], area.alignBlock) : null;
|
|
if (inlinePoint) {
|
|
drawAlignment(context, inlinePoint, areaBounds[i]);
|
|
}
|
|
if (blockPoint) {
|
|
drawAlignment(context, blockPoint, areaBounds[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
function drawSnapportBorder(
|
|
highlight: ScrollSnapHighlight, context: CanvasRenderingContext2D, emulationScaleFactor: number) {
|
|
drawPath(
|
|
context, highlight.snapport, undefined, highlight.snapportBorder.color, undefined, emptyBounds(),
|
|
emulationScaleFactor);
|
|
}
|
|
|
|
export function drawScrollSnapHighlight(
|
|
highlight: ScrollSnapHighlight, context: CanvasRenderingContext2D, emulationScaleFactor: number) {
|
|
// The order of the following draw calls is important, change it carefully.
|
|
drawScrollPadding(highlight, context, emulationScaleFactor);
|
|
const areaBounds = drawSnapAreas(highlight, context, emulationScaleFactor);
|
|
drawSnapportBorder(highlight, context, emulationScaleFactor);
|
|
drawAlignmentPoints(areaBounds, highlight, context);
|
|
}
|