/
githubmr
/
facebook-react
Обзор
Документация
Войти
/
githubmr
/
facebook-react
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
packages/react-devtools-timeline/src/content-views/SnapshotsView.js
260 строк
7 KB
Jan Kassens
[flow] make Flow suppressions explicit on the error (#26487)
27 мар 2023, 14:43
Не верифицирован
27 мар 2023, 14:43
afea1d0
Код
Авторство
О чём код?
/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow */ import type {Snapshot, TimelineData} from '../types'; import type { Interaction, Point, Rect, Size, Surface, ViewRefs, } from '../view-base'; import {positioningScaleFactor, timestampToPosition} from './utils/positioning'; import { intersectionOfRects, rectContainsPoint, rectEqualToRect, View, } from '../view-base'; import {BORDER_SIZE, COLORS, SNAPSHOT_SCRUBBER_SIZE} from './constants'; type OnHover = (node: Snapshot | null) => void; export class SnapshotsView extends View { _hoverLocation: Point | null = null; _intrinsicSize: Size; _profilerData: TimelineData; onHover: OnHover | null = null; constructor(surface: Surface, frame: Rect, profilerData: TimelineData) { super(surface, frame); this._intrinsicSize = { width: profilerData.duration, height: profilerData.snapshotHeight, }; this._profilerData = profilerData; } desiredSize(): Size { return this._intrinsicSize; } draw(context: CanvasRenderingContext2D) { const snapshotHeight = this._profilerData.snapshotHeight; const {visibleArea} = this; context.fillStyle = COLORS.BACKGROUND; context.fillRect( visibleArea.origin.x, visibleArea.origin.y, visibleArea.size.width, visibleArea.size.height, ); const y = visibleArea.origin.y; let x = visibleArea.origin.x; // Rather than drawing each snapshot where it occurred, // draw them at fixed intervals and just show the nearest one. while (x < visibleArea.origin.x + visibleArea.size.width) { const snapshot = this._findClosestSnapshot(x); if (snapshot === null) { // This shold never happen. break; } const scaledHeight = snapshotHeight; const scaledWidth = (snapshot.width * snapshotHeight) / snapshot.height; const imageRect: Rect = { origin: { x, y, }, size: {width: scaledWidth, height: scaledHeight}, }; // Lazily create and cache Image objects as we render a snapsho for the first time. if (snapshot.image === null) { const img = (snapshot.image = new Image()); img.onload = () => { this._drawSnapshotImage(context, snapshot, imageRect); }; img.src = snapshot.imageSource; } else { this._drawSnapshotImage(context, snapshot, imageRect); } x += scaledWidth + BORDER_SIZE; } const hoverLocation = this._hoverLocation; if (hoverLocation !== null) { const scrubberWidth = SNAPSHOT_SCRUBBER_SIZE + BORDER_SIZE * 2; const scrubberOffset = scrubberWidth / 2; context.fillStyle = COLORS.SCRUBBER_BORDER; context.fillRect( hoverLocation.x - scrubberOffset, visibleArea.origin.y, scrubberWidth, visibleArea.size.height, ); context.fillStyle = COLORS.SCRUBBER_BACKGROUND; context.fillRect( hoverLocation.x - scrubberOffset + BORDER_SIZE, visibleArea.origin.y, SNAPSHOT_SCRUBBER_SIZE, visibleArea.size.height, ); } } handleInteraction(interaction: Interaction, viewRefs: ViewRefs) { switch (interaction.type) { case 'mousemove': case 'wheel-control': case 'wheel-meta': case 'wheel-plain': case 'wheel-shift': this._updateHover(interaction.payload.location, viewRefs); break; } } _drawSnapshotImage( context: CanvasRenderingContext2D, snapshot: Snapshot, imageRect: Rect, ) { const visibleArea = this.visibleArea; // Prevent snapshot from visibly overflowing its container when clipped. // View clips by default, but since this view may draw async (on Image load) we re-clip. const shouldClip = !rectEqualToRect(imageRect, visibleArea); if (shouldClip) { const clippedRect = intersectionOfRects(imageRect, visibleArea); context.save(); context.beginPath(); context.rect( clippedRect.origin.x, clippedRect.origin.y, clippedRect.size.width, clippedRect.size.height, ); context.closePath(); context.clip(); } context.fillStyle = COLORS.REACT_RESIZE_BAR_BORDER; context.fillRect( imageRect.origin.x, imageRect.origin.y, imageRect.size.width, imageRect.size.height, ); // $FlowFixMe[incompatible-call] Flow doesn't know about the 9 argument variant of drawImage() context.drawImage( snapshot.image, // Image coordinates 0, 0, // Native image size snapshot.width, snapshot.height, // Canvas coordinates imageRect.origin.x + BORDER_SIZE, imageRect.origin.y + BORDER_SIZE, // Scaled image size imageRect.size.width - BORDER_SIZE * 2, imageRect.size.height - BORDER_SIZE * 2, ); if (shouldClip) { context.restore(); } } _findClosestSnapshot(x: number): Snapshot | null { const frame = this.frame; const scaleFactor = positioningScaleFactor( this._intrinsicSize.width, frame, ); const snapshots = this._profilerData.snapshots; let startIndex = 0; let stopIndex = snapshots.length - 1; while (startIndex <= stopIndex) { const currentIndex = Math.floor((startIndex + stopIndex) / 2); const snapshot = snapshots[currentIndex]; const {timestamp} = snapshot; const snapshotX = Math.floor( timestampToPosition(timestamp, scaleFactor, frame), ); if (x < snapshotX) { stopIndex = currentIndex - 1; } else { startIndex = currentIndex + 1; } } return snapshots[stopIndex] || null; } /** * @private */ _updateHover(location: Point, viewRefs: ViewRefs) { const {onHover, visibleArea} = this; if (!onHover) { return; } if (!rectContainsPoint(location, visibleArea)) { if (this._hoverLocation !== null) { this._hoverLocation = null; this.setNeedsDisplay(); } onHover(null); return; } const snapshot = this._findClosestSnapshot(location.x); if (snapshot !== null) { this._hoverLocation = location; onHover(snapshot); } else { this._hoverLocation = null; onHover(null); } // Any time the mouse moves within the boundaries of this view, we need to re-render. // This is because we draw a scrubbing bar that shows the location corresponding to the current tooltip. this.setNeedsDisplay(); } }