Avoid UI flickering during transition from prerendered to live content
This commit is contained in:
parent
6a57218e14
commit
5315446054
|
|
@ -7,6 +7,7 @@ const selectValuePropname = '_blazorSelectValue';
|
||||||
const sharedTemplateElemForParsing = document.createElement('template');
|
const sharedTemplateElemForParsing = document.createElement('template');
|
||||||
const sharedSvgElemForParsing = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
const sharedSvgElemForParsing = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
||||||
const preventDefaultEvents: { [eventType: string]: boolean } = { submit: true };
|
const preventDefaultEvents: { [eventType: string]: boolean } = { submit: true };
|
||||||
|
const rootComponentsPendingFirstRender: { [componentId: number]: Element } = {};
|
||||||
|
|
||||||
export class BrowserRenderer {
|
export class BrowserRenderer {
|
||||||
private eventDelegator: EventDelegator;
|
private eventDelegator: EventDelegator;
|
||||||
|
|
@ -19,7 +20,9 @@ export class BrowserRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public attachRootComponentToElement(componentId: number, element: Element) {
|
public attachRootComponentToElement(componentId: number, element: Element) {
|
||||||
this.attachComponentToElement(componentId, toLogicalElement(element));
|
// 'allowExistingContents' to keep any prerendered content until we do the first client-side render
|
||||||
|
this.attachComponentToElement(componentId, toLogicalElement(element, /* allowExistingContents */ true));
|
||||||
|
rootComponentsPendingFirstRender[componentId] = element;
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateComponent(batch: RenderBatch, componentId: number, edits: ArraySegment<RenderTreeEdit>, referenceFrames: ArrayValues<RenderTreeFrame>) {
|
public updateComponent(batch: RenderBatch, componentId: number, edits: ArraySegment<RenderTreeEdit>, referenceFrames: ArrayValues<RenderTreeFrame>) {
|
||||||
|
|
@ -28,6 +31,13 @@ export class BrowserRenderer {
|
||||||
throw new Error(`No element is currently associated with component ${componentId}`);
|
throw new Error(`No element is currently associated with component ${componentId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On the first render for each root component, clear any existing content (e.g., prerendered)
|
||||||
|
const rootElementToClear = rootComponentsPendingFirstRender[componentId];
|
||||||
|
if (rootElementToClear) {
|
||||||
|
delete rootComponentsPendingFirstRender[componentId];
|
||||||
|
clearElement(rootElementToClear);
|
||||||
|
}
|
||||||
|
|
||||||
this.applyEdits(batch, element, 0, edits, referenceFrames);
|
this.applyEdits(batch, element, 0, edits, referenceFrames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -368,3 +378,10 @@ function raiseEvent(event: Event, browserRendererId: number, eventHandlerId: num
|
||||||
eventDescriptor,
|
eventDescriptor,
|
||||||
JSON.stringify(eventArgs.data));
|
JSON.stringify(eventArgs.data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clearElement(element: Element) {
|
||||||
|
let childNode: Node | null;
|
||||||
|
while (childNode = element.firstChild) {
|
||||||
|
element.removeChild(childNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,12 @@
|
||||||
const logicalChildrenPropname = createSymbolOrFallback('_blazorLogicalChildren');
|
const logicalChildrenPropname = createSymbolOrFallback('_blazorLogicalChildren');
|
||||||
const logicalParentPropname = createSymbolOrFallback('_blazorLogicalParent');
|
const logicalParentPropname = createSymbolOrFallback('_blazorLogicalParent');
|
||||||
|
|
||||||
export function toLogicalElement(element: Element) {
|
export function toLogicalElement(element: Element, allowExistingContents?: boolean) {
|
||||||
if (element.childNodes.length > 0) {
|
// Normally it's good to assert that the element has started empty, because that's the usual
|
||||||
throw new Error('New logical elements must start empty');
|
// situation and we probably have a bug if it's not. But for the element that contain prerendered
|
||||||
|
// root components, we want to let them keep their content until we replace it.
|
||||||
|
if (element.childNodes.length > 0 && !allowExistingContents) {
|
||||||
|
throw new Error('New logical elements must start empty, or allowExistingContents must be true');
|
||||||
}
|
}
|
||||||
|
|
||||||
element[logicalChildrenPropname] = [];
|
element[logicalChildrenPropname] = [];
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ export function attachRootComponentToElement(browserRendererId: number, elementS
|
||||||
if (!browserRenderer) {
|
if (!browserRenderer) {
|
||||||
browserRenderer = browserRenderers[browserRendererId] = new BrowserRenderer(browserRendererId);
|
browserRenderer = browserRenderers[browserRendererId] = new BrowserRenderer(browserRendererId);
|
||||||
}
|
}
|
||||||
clearElement(element);
|
|
||||||
browserRenderer.attachRootComponentToElement(componentId, element);
|
browserRenderer.attachRootComponentToElement(componentId, element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,10 +56,3 @@ export function renderBatch(browserRendererId: number, batch: RenderBatch) {
|
||||||
browserRenderer.disposeEventHandler(eventHandlerId);
|
browserRenderer.disposeEventHandler(eventHandlerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearElement(element: Element) {
|
|
||||||
let childNode: Node | null;
|
|
||||||
while (childNode = element.firstChild) {
|
|
||||||
element.removeChild(childNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue