LynxMainThreadEvent

A directive that binds main-thread scripts to an element's native events, and passes them elements they can change directly. import { LynxMainThreadEvent } from '@blotch/angular-lynx';

Info

Template handlers such as (bindtap) already run on the main thread. Main-thread scripts explains when you need this API instead.

Usage

src/app/app.ts
import { Component } from '@angular/core';
import {
  LYNX_ELEMENTS,
  LynxMainThreadEvent,
  mainThreadFn,
  type MainThread,
} from '@blotch/angular-lynx';

const follow = mainThreadFn((event: MainThread.TouchEvent) => {
  const touch = event.touches[0];
  if (!touch) return;
  event.currentTarget.setStyleProperty('opacity', String(touch.clientX / 300));
});

const reset = mainThreadFn((event: MainThread.TouchEvent) => {
  event.currentTarget.setStyleProperty('opacity', '1');
});

@Component({
  selector: 'app-root',
  imports: [LYNX_ELEMENTS, LynxMainThreadEvent],
  template: `
    <view [mainThreadBindtouchmove]="follow" [mainThreadBindtouchend]="reset">
      <text>Drag across me</text>
    </view>
  `,
})
export class App {
  readonly follow = follow;
  readonly reset = reset;
}

API

Inputs

class LynxMainThreadEvent {
  mainThreadBindtap?: MainThreadFnHandle | null;
  mainThreadCatchtap?: MainThreadFnHandle | null;
  // … one input per cell in the table below
}

Each input takes a handle from mainThreadFn(), or a function with the 'main thread' directive. Bind inputs let the event bubble to ancestors. Catch inputs stop it at this element.

EventBubblesStops bubblingHandler's event type
tapmainThreadBindtapmainThreadCatchtapMainThread.TouchEvent
longpressmainThreadBindlongpressmainThreadCatchlongpressMainThread.TouchEvent
touchstartmainThreadBindtouchstartmainThreadCatchtouchstartMainThread.TouchEvent
touchmovemainThreadBindtouchmovemainThreadCatchtouchmoveMainThread.TouchEvent
touchendmainThreadBindtouchendmainThreadCatchtouchendMainThread.TouchEvent
mousedownmainThreadBindmousedownmainThreadCatchmousedownMainThread.MouseEvent
mousemovemainThreadBindmousemovemainThreadCatchmousemoveMainThread.MouseEvent
mouseupmainThreadBindmouseupmainThreadCatchmouseupMainThread.MouseEvent
scrollmainThreadBindscrollmainThreadCatchscrollNo MainThread type
animationstartmainThreadBindanimationstart—MainThread.AnimationEvent
animationendmainThreadBindanimationend—MainThread.AnimationEvent
transitionendmainThreadBindtransitionend—MainThread.TransitionEvent
layoutchangemainThreadBindlayoutchange—MainThread.LayoutChangeEvent

In the handler, event.target and event.currentTarget are MainThread.Element objects. When the element is destroyed, its handlers are removed.

Constraints

  • The web preview fires mouse events, not touch events. A desktop browser never fires touch events, so bind the mouse* inputs too for a drag that works there. Phones fire touch events, plus a mousedown and mouseup after a tap.
  • A handler doesn't run change detection. Change the element directly, through event.currentTarget.
  • Each input takes one handler. Binding a new one replaces the old one.

Types

import type { MainThread } from '@blotch/angular-lynx';

MainThread is a namespace of the types from @lynx-js/types/main-thread:

TypeWhat it is
MainThread.ElementAn element you can change directly (methods below)
MainThread.TouchEventtap, longpress and touch* events. Has touches, changedTouches
MainThread.MouseEventmouse* events
MainThread.WheelEventWheel events
MainThread.KeyEventKey events
MainThread.AnimationEventanimationstart, animationend
MainThread.TransitionEventtransitionend
MainThread.LayoutChangeEventlayoutchange
MainThread.UIAppearanceEventAppear and disappear events
// MainThread.Element
interface Element {
  getAttribute(name: string): any;
  getAttributeNames(): string[];
  setAttribute(name: string, value: any): void;
  setStyleProperty(name: string, value: string): void;
  setStyleProperties(styles: Record<string, string>): void;
  querySelector(selector: string): Element | null;
  querySelectorAll(selector: string): Element[];
  invoke(methodName: string, params?: Record<string, any>): Promise<any>;
  animate(
    keyframes: Record<string, number | string>[],
    options?: number | AnimationOptions,
  ): Animation;
}
  • setStyleProperty() and setAttribute() batch their changes, and Lynx applies them together after your handler returns.
  • invoke() calls one of the element's UI methods, such as scrollTo on a <scroll-view>, and resolves with its result.
  • getBoundingClientRect() also exists at runtime (AngularLynx's MainThreadElement class adds it), but isn't on the type. It returns { left, top, right, bottom, width, height } built from the computed width, height, left and top styles. So left and top are the CSS values, often 0, not the element's position on screen.