Switch to Canvas Rendering

This switches the rendering of the layout from using HTML and CSS to
using a canvas (technically it's two). This not only should improve the
performance but also brings the rendering in line with our native
renderers, enabling new features such as vertical resizing and
horizontal layouts. This also removes a lot of code specific to the HTML
rendering and thus greatly simplifies the codebase. There is one slight
regression in that the layout editor doesn't allow clicking and dragging
the individual components directly on the layout anymore. This is
however something we can explore directly in `livesplit-core` in the
future, so other frontends can benefit from it as well.
This commit is contained in:
Christopher Serr
2024-04-27 21:45:41 +02:00
parent 78a668f9e5
commit 3e249de680
37 changed files with 193 additions and 1880 deletions
-6
View File
@@ -1,6 +0,0 @@
.abbreviated {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width: 100%;
}
-59
View File
@@ -1,59 +0,0 @@
@import 'Time';
@import 'variables';
.detailed-timer {
position: relative;
@include time;
.detailed-timer-left-side {
position: absolute;
display: flex;
top: 0;
left: $side-padding;
height: 100%;
width: 50%;
.detailed-timer-icon-container {
height: 100%;
padding: $vertical-padding $side-padding;
margin-right: $side-padding;
flex-shrink: 0;
box-sizing: border-box;
.detailed-timer-icon-inner-container {
width: 100%;
height: 100%;
text-align: center;
vertical-align: center;
.detailed-timer-icon {
max-width: 100%;
height: 100%;
object-fit: contain;
}
}
}
.detailed-timer-left-side-text {
position: relative;
width: 100%;
.detailed-timer-segment-name {
position: absolute;
top: $vertical-padding;
left: 0;
width: 100%;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.detailed-timer-comparisons {
position: absolute;
left: 0;
bottom: $vertical-padding;
}
}
}
}
-3
View File
@@ -1,3 +0,0 @@
.graph {
display: block;
}
-59
View File
@@ -1,59 +0,0 @@
@import 'Time';
@import 'variables';
.key-value {
height: $default-component-height;
padding: 0 $side-padding;
display: flex;
@include time;
&.key-value-two-rows {
height: $two-row-height;
padding-top: $vertical-padding;
padding-bottom: $vertical-padding;
flex-wrap: wrap;
align-items: stretch;
.key-value-value {
width: 100%;
}
}
&.key-value-center {
justify-content: center;
text-align: center;
.key-value-key {
justify-content: center;
}
}
&.key-value-left-right {
.key-value-key {
padding-right: $side-padding;
}
}
.key-value-key {
display: flex;
flex-grow: 1;
justify-content: flex-start;
align-items: center;
overflow: hidden;
}
.key-value-value {
display: flex;
flex-shrink: 0;
justify-content: flex-end;
align-items: center;
overflow: hidden;
.key-value-value-inner {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
+30 -15
View File
@@ -1,27 +1,42 @@
@import 'variables';
@import 'Font';
.layout {
font-family: "fira", sans-serif;
font-size: $default-font-size;
text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.5);
position: relative;
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
.resizable-layout {
position: absolute;
z-index: 2;
height: 100% !important;
top: 0;
width: inherit;
height: inherit;
.resizable-handle {
.react-resizable {
width: 0 !important;
height: 0 !important;
}
.resizable-handle-east {
cursor: e-resize;
bottom: -4px;
right: -4px;
right: -10px;
bottom: 10px;
top: 0;
position: absolute;
width: 20px;
height: 100%;
}
.resizable-handle-south {
cursor: s-resize;
bottom: -10px;
left: 0;
right: 10px;
position: absolute;
height: 20px;
}
.resizable-handle-south-east {
cursor: se-resize;
bottom: -10px;
right: -10px;
position: absolute;
width: 20px;
height: 20px;
}
}
}
-6
View File
@@ -1,6 +0,0 @@
@import 'variables';
.separator {
background-color: hsla(0, 0%, 43%, 1);
height: $thick-separator-width;
}
-160
View File
@@ -1,160 +0,0 @@
@use 'sass:math';
@import "Time";
@import 'variables';
$split-column-width: $default-line-height * 3;
$split-label-height: $default-component-height - $thick-separator-width;
$split-icon-size: $default-component-height - $vertical-padding * 2;
$split-icon-two-row-size: $two-row-height - $vertical-padding * 2;
.splits {
width: 100%;
position: relative;
.split {
display: flex;
position: relative;
width: 100%;
height: $default-component-height;
@include time;
&.two-rows {
height: $two-row-height;
.split-rows {
flex-wrap: wrap;
padding-top: $vertical-padding;
padding-bottom: $vertical-padding;
.split-row {
height: initial;
&.split-second-row {
width: 100%;
justify-content: flex-end;
}
}
}
.split-icon-container {
width: $split-icon-two-row-size;
}
}
.split-icon-container {
padding: $vertical-padding $side-padding;
width: $split-icon-size;
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: center;
position: relative;
.split-icon {
width: 100%;
height: 100%;
object-fit: contain;
filter: drop-shadow(2px 2px 1px rgba(0, 0, 0, 0.3));
}
}
.split-icon-container-empty {
padding-right: $side-padding;
position: relative;
}
.split-rows {
display: flex;
align-items: center;
flex-grow: 1;
overflow: hidden;
height: 100%;
position: relative;
box-sizing: border-box;
.split-row {
display: flex;
align-items: center;
overflow: hidden;
position: relative;
height: 100%;
&.split-first-row,
.split-name {
flex-grow: 1;
}
&.split-second-row,
.split-time {
flex-shrink: 0;
}
.split-name {
display: flex;
align-items: center;
justify-content: flex-start;
overflow: hidden;
padding: 0 $side-padding;
padding-left: 0;
height: 100%;
.split-name-inner {
padding: 0;
max-width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.split-time {
display: flex;
align-items: center;
justify-content: flex-end;
padding: 0 $side-padding;
padding-left: 0;
height: 100%;
&.split-time-full {
width: $split-column-width;
flex-shrink: 0;
}
.split-time-inner {
padding: 0;
}
}
}
}
&.split-label {
height: $split-label-height;
}
.current-split-background,
.split-borders {
position: absolute;
width: 100%;
height: 100%;
top: 0;
padding: 0;
box-sizing: border-box;
}
.split-borders {
border-bottom-width: $thin-separator-width;
border-top-width: $thin-separator-width;
border-bottom-style: solid;
border-top-style: solid;
top: math.div(-$thick-separator-width, 2);
height: calc(100% + #{math.div($thick-separator-width, 2)});
&.separator-above {
border-top-width: $thick-separator-width;
border-top-style: solid;
}
}
}
}
-5
View File
@@ -1,5 +0,0 @@
@mixin time {
.time {
font-variant-numeric: tabular-nums;
}
}
-4
View File
@@ -1,4 +0,0 @@
.timer {
will-change: transform;
transform: translateZ(0);
}
-99
View File
@@ -1,99 +0,0 @@
@import "variables";
$game-icon-size: $two-row-height - $vertical-padding * 2;
.title {
height: $two-row-height;
width: 100%;
display: flex;
padding-top: $vertical-padding;
padding-bottom: $vertical-padding;
.game-icon-container {
max-width: $game-icon-size;
max-height: $game-icon-size;
padding: $vertical-padding 0;
padding-left: $side-padding;
flex-shrink: 0;
.title-icon {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.title-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.run-info {
height: 100%;
flex-grow: 1;
overflow: hidden;
.title-game {
display: flex;
align-items: center;
height: 50%;
padding: 0 $side-padding;
&.centered .abbreviated {
text-align: center;
}
}
.lower-row {
display: flex;
position: relative;
height: 50%;
&.centered {
justify-content: center;
}
&.one-line {
height: 100%;
}
.title-category {
display: flex;
align-items: center;
height: 100%;
flex-grow: 1;
overflow: hidden;
padding: 0 $side-padding;
&.centered .abbreviated {
text-align: center;
}
}
.title-attempts,
.title-attempts-invisible {
height: 100%;
flex-shrink: 0;
display: flex;
align-items: center;
&.one-line {
height: 50%;
align-self: flex-end;
}
}
.title-attempts {
justify-content: flex-end;
padding-right: $side-padding;
}
.title-attempts-invisible {
justify-content: flex-start;
padding-left: $side-padding;
visibility: hidden;
}
}
}
}