0dc2548d0b
* Optimize simulator for light mode. * Add user-select none.
211 lines
9.0 KiB
TypeScript
211 lines
9.0 KiB
TypeScript
/// <reference path="./moduleView.ts" />
|
|
|
|
namespace pxsim.visuals {
|
|
|
|
export class BrickView extends ModuleView implements LayoutElement {
|
|
|
|
private static EV3_SCREEN_ID = "ev3_screen";
|
|
private static EV3_LIGHT_ID = "btn_color";
|
|
|
|
private buttons: SVGElement[];
|
|
private light: SVGElement;
|
|
|
|
private currentCanvasX = 178;
|
|
private currentCanvasY = 128;
|
|
|
|
private static LIGHT_BLACK_COLOR = '#6a6a6a';
|
|
private static LIGHT_RED_COLOR = '#6a6a6a';
|
|
|
|
constructor(port: number) {
|
|
super(EV3_SVG, "board", NodeType.Brick, port);
|
|
}
|
|
|
|
protected buildDomCore() {
|
|
// Setup buttons
|
|
const btnids = ["btn_up", "btn_enter", "btn_down", "btn_right", "btn_left", "btn_back"];
|
|
this.buttons = btnids.map(n => this.content.getElementById(this.normalizeId(n)) as SVGElement);
|
|
this.buttons.forEach(b => svg.addClass(b, "sim-button"));
|
|
|
|
this.light = this.content.getElementById(this.normalizeId(BrickView.EV3_LIGHT_ID)) as SVGElement;
|
|
}
|
|
|
|
protected optimizeForLightMode() {
|
|
(this.content.getElementById(this.normalizeId('ev3_body_2')) as SVGElement).style.fill = '#f1f1f1';
|
|
(this.content.getElementById(this.normalizeId('ev3_screen_grey')) as SVGElement).style.fill = '#a8aaa8';
|
|
(this.content.getElementById(this.normalizeId('ev3_grey_buttom')) as SVGElement).style.fill = '#a8aaa8';
|
|
(this.content.getElementById(this.normalizeId('btn_part_2')) as SVGElement).style.fill = '#393939';
|
|
}
|
|
|
|
private setStyleFill(svgId: string, fillUrl: string, lightFill?: string) {
|
|
const el = (this.content.getElementById(svgId) as SVGRectElement);
|
|
if (el) el.style.fill = inLightMode() ? lightFill || BrickView.LIGHT_BLACK_COLOR : `url("#${fillUrl}")`;
|
|
}
|
|
|
|
public hasClick() {
|
|
return false;
|
|
}
|
|
|
|
public updateState() {
|
|
this.updateLight();
|
|
}
|
|
|
|
public updateThemeCore() {
|
|
let theme = this.theme;
|
|
// svg.fill(this.buttons[0], theme.buttonUps[0]);
|
|
// svg.fill(this.buttons[1], theme.buttonUps[1]);
|
|
// svg.fill(this.buttons[2], theme.buttonUps[2]);
|
|
}
|
|
|
|
private lastLightPattern: number = -1;
|
|
private lastLightAnimationId: any = undefined;
|
|
private updateLight() {
|
|
let state = ev3board().getBrickNode().lightState;
|
|
|
|
const lightPattern = state.lightPattern;
|
|
if (lightPattern == this.lastLightPattern) return;
|
|
this.lastLightPattern = lightPattern;
|
|
if (this.lastLightAnimationId) {
|
|
cancelAnimationFrame(this.lastLightAnimationId);
|
|
delete this.lastLightAnimationId;
|
|
}
|
|
switch (lightPattern) {
|
|
case 0: // LED_BLACK
|
|
this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-black`));
|
|
//svg.fill(this.light, "#FFF");
|
|
break;
|
|
case 1: // LED_GREEN
|
|
this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-green`), 'green');
|
|
//svg.fill(this.light, "#00ff00");
|
|
break;
|
|
case 2: // LED_RED
|
|
this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-red`), 'red');
|
|
//svg.fill(this.light, "#ff0000");
|
|
break;
|
|
case 3: // LED_ORANGE
|
|
this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-orange`), 'orange');
|
|
//svg.fill(this.light, "#FFA500");
|
|
break;
|
|
case 4: // LED_GREEN_FLASH
|
|
this.flashLightAnimation('green');
|
|
break;
|
|
case 5: // LED_RED_FLASH
|
|
this.flashLightAnimation('red');
|
|
break;
|
|
case 6: // LED_ORANGE_FLASH
|
|
this.flashLightAnimation('orange');
|
|
break;
|
|
case 7: // LED_GREEN_PULSE
|
|
this.pulseLightAnimation('green');
|
|
break;
|
|
case 8: // LED_RED_PULSE
|
|
this.pulseLightAnimation('red');
|
|
break;
|
|
case 9: // LED_ORANGE_PULSE
|
|
this.pulseLightAnimation('orange');
|
|
break;
|
|
}
|
|
}
|
|
|
|
private flashLightAnimation(id: string) {
|
|
const pattern = this.lastLightPattern;
|
|
let fps = 3;
|
|
let now;
|
|
let then = Date.now();
|
|
let interval = 1000 / fps;
|
|
let delta;
|
|
let that = this;
|
|
function draw() {
|
|
if (that.lastLightPattern != pattern) return;
|
|
that.lastLightAnimationId = requestAnimationFrame(draw);
|
|
now = pxsim.U.now();
|
|
delta = now - then;
|
|
if (delta > interval) {
|
|
then = now - (delta % interval);
|
|
that.flashLightAnimationStep(id);
|
|
}
|
|
}
|
|
draw();
|
|
}
|
|
|
|
private flash: boolean;
|
|
private flashLightAnimationStep(id: string) {
|
|
if (this.flash) {
|
|
this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-${id}`), id);
|
|
} else {
|
|
this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-black`));
|
|
}
|
|
this.flash = !this.flash;
|
|
}
|
|
|
|
private pulseLightAnimation(id: string) {
|
|
const pattern = this.lastLightPattern;
|
|
let fps = 8;
|
|
let now;
|
|
let then = Date.now();
|
|
let interval = 1000 / fps;
|
|
let delta;
|
|
let that = this;
|
|
function draw() {
|
|
if (that.lastLightPattern != pattern) return;
|
|
that.lastLightAnimationId = requestAnimationFrame(draw);
|
|
now = pxsim.U.now();
|
|
delta = now - then;
|
|
if (delta > interval) {
|
|
// update time stuffs
|
|
then = now - (delta % interval);
|
|
that.pulseLightAnimationStep(id);
|
|
}
|
|
}
|
|
draw();
|
|
}
|
|
|
|
private pulse: number = 0;
|
|
private pulseLightAnimationStep(id: string) {
|
|
switch (this.pulse) {
|
|
case 1: this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-black`)); break;
|
|
case 2: this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-black`)); break;
|
|
case 3: this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-black`)); break;
|
|
case 4: this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-black`)); break;
|
|
case 5: this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-${id}`), id); break;
|
|
case 6: this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-${id}`), id); break;
|
|
case 7: this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-black`)); break;
|
|
case 8: this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-${id}`), id); break;
|
|
|
|
}
|
|
this.pulse++;
|
|
if (this.pulse == 9) this.pulse = 0;
|
|
}
|
|
|
|
public kill() {
|
|
cancelAnimationFrame(this.lastLightAnimationId);
|
|
}
|
|
|
|
public attachEvents() {
|
|
let bpState = ev3board().getBrickNode().buttonState;
|
|
let stateButtons = bpState.buttons;
|
|
this.buttons.forEach((btn, index) => {
|
|
let button = stateButtons[index];
|
|
|
|
pointerEvents.down.forEach(evid => btn.addEventListener(evid, ev => {
|
|
button.setPressed(true);
|
|
svg.fill(this.buttons[index], this.theme.buttonDown);
|
|
}));
|
|
btn.addEventListener(pointerEvents.leave, ev => {
|
|
button.setPressed(false);
|
|
svg.fill(this.buttons[index], this.theme.buttonUps[index]);
|
|
})
|
|
btn.addEventListener(pointerEvents.up, ev => {
|
|
button.setPressed(false);
|
|
svg.fill(this.buttons[index], this.theme.buttonUps[index]);
|
|
})
|
|
})
|
|
}
|
|
|
|
public getScreenBBox() {
|
|
if (!this.content) return undefined;
|
|
const screen = this.content.getElementById(this.normalizeId(BrickView.EV3_SCREEN_ID));
|
|
if (!screen) return undefined;
|
|
return screen.getBoundingClientRect();
|
|
}
|
|
}
|
|
} |