diff --git a/site/content/examples/parallax/App.html b/site/content/examples/parallax/App.html index fa2399d6f8..c7781e7a3f 100644 --- a/site/content/examples/parallax/App.html +++ b/site/content/examples/parallax/App.html @@ -1,3 +1,7 @@ + + diff --git a/site/src/routes/index.html b/site/src/routes/index.html index f058be3a82..9cb7be24f8 100644 --- a/site/src/routes/index.html +++ b/site/src/routes/index.html @@ -112,7 +112,7 @@ Svelte • The magical disappearing UI framework - + diff --git a/site/static/workers/compiler.js b/site/static/workers/compiler.js index 7759c492a3..c20b416588 100644 --- a/site/static/workers/compiler.js +++ b/site/static/workers/compiler.js @@ -21,7 +21,8 @@ self.addEventListener('message', async event => { }); const commonCompilerOptions = { - dev: false + dev: false, + css: false }; function compile({ source, options, entry }) { diff --git a/src/compile/render-dom/wrappers/Window.ts b/src/compile/render-dom/wrappers/Window.ts index 29e8ab82d3..98e4e65f65 100644 --- a/src/compile/render-dom/wrappers/Window.ts +++ b/src/compile/render-dom/wrappers/Window.ts @@ -68,9 +68,9 @@ export default class WindowWrapper extends Wrapper { }); }); - const lock = block.getUniqueName(`window_updating`); - const clear = block.getUniqueName(`clear_window_updating`); - const timeout = block.getUniqueName(`window_updating_timeout`); + const scrolling = block.getUniqueName(`scrolling`); + const clear_scrolling = block.getUniqueName(`clear_scrolling`); + const scrolling_timeout = block.getUniqueName(`scrolling_timeout`); Object.keys(events).forEach(event => { const handler_name = block.getUniqueName(`onwindow${event}`); @@ -78,9 +78,9 @@ export default class WindowWrapper extends Wrapper { if (event === 'scroll') { // TODO other bidirectional bindings... - block.addVariable(lock, 'false'); - block.addVariable(clear, `function() { ${lock} = false; }`); - block.addVariable(timeout); + block.addVariable(scrolling, 'false'); + block.addVariable(clear_scrolling, `() => { ${scrolling} = false }`); + block.addVariable(scrolling_timeout); const condition = [ bindings.scrollX && `"${bindings.scrollX}" in this._state`, @@ -97,58 +97,61 @@ export default class WindowWrapper extends Wrapper { ${x && `${x} = window.pageXOffset;`} ${y && `${y} = window.pageYOffset;`} `); + + block.event_listeners.push(deindent` + @addListener(window, "${event}", () => { + ${scrolling} = true; + clearTimeout(${scrolling_timeout}); + ${scrolling_timeout} = setTimeout(${clear_scrolling}, 100); + ctx.${handler_name}(); + }) + `); } else { props.forEach(prop => { renderer.metaBindings.addLine( `this._state.${prop.name} = window.${prop.value};` ); }); + + block.event_listeners.push(deindent` + @addListener(window, "${event}", ctx.${handler_name}); + `); } component.declarations.push(handler_name); component.template_references.add(handler_name); component.partly_hoisted.push(deindent` function ${handler_name}() { - ${event === 'scroll' && deindent` - if (${lock}) return; - ${lock} = true; - `} ${props.map(prop => `${prop.name} = window.${prop.value}; $$make_dirty('${prop.name}');`)} - ${event === 'scroll' && `${lock} = false;`} } `); + + block.builders.init.addBlock(deindent` - window.addEventListener("${event}", ctx.${handler_name}); @add_render_callback(ctx.${handler_name}); `); - block.builders.destroy.addBlock(deindent` - window.removeEventListener("${event}", ctx.${handler_name}); - `); - component.has_reactive_assignments = true; }); // special case... might need to abstract this out if we add more special cases if (bindings.scrollX || bindings.scrollY) { - block.builders.init.addBlock(deindent` - #component.$on("state", ({ changed, current }) => { - if (${ - [bindings.scrollX, bindings.scrollY].map( - binding => binding && `changed["${binding}"]` - ).filter(Boolean).join(' || ') - } && !${lock}) { - ${lock} = true; - clearTimeout(${timeout}); - window.scrollTo(${ - bindings.scrollX ? `current["${bindings.scrollX}"]` : `window.pageXOffset` - }, ${ - bindings.scrollY ? `current["${bindings.scrollY}"]` : `window.pageYOffset` - }); - ${timeout} = setTimeout(${clear}, 100); - } - }); + block.builders.update.addBlock(deindent` + if (${ + [bindings.scrollX, bindings.scrollY].filter(Boolean).map( + b => `changed.${b}` + ).join(' || ') + } && !${scrolling}) { + ${scrolling} = true; + clearTimeout(${scrolling_timeout}); + window.scrollTo(${ + bindings.scrollX ? `current["${bindings.scrollX}"]` : `window.pageXOffset` + }, ${ + bindings.scrollY ? `current["${bindings.scrollY}"]` : `window.pageYOffset` + }); + ${scrolling_timeout} = setTimeout(${clear_scrolling}, 100); + } `); } diff --git a/test/js/samples/window-binding-scroll/expected.js b/test/js/samples/window-binding-scroll/expected.js index fee92a8d45..0600a211ee 100644 --- a/test/js/samples/window-binding-scroll/expected.js +++ b/test/js/samples/window-binding-scroll/expected.js @@ -1,26 +1,22 @@ /* generated by Svelte vX.Y.Z */ -import { SvelteComponent as SvelteComponent_1, add_render_callback, append, createElement, createText, detachNode, flush, init, insert, run, safe_not_equal, setData } from "svelte/internal"; +import { SvelteComponent as SvelteComponent_1, addListener, add_render_callback, append, createElement, createText, detachNode, flush, init, insert, run, safe_not_equal, setData } from "svelte/internal"; function create_fragment(component, ctx) { - var window_updating = false, clear_window_updating = function() { window_updating = false; }, window_updating_timeout, p, text0, text1, current; + var scrolling = false, clear_scrolling = () => { scrolling = false }, scrolling_timeout, p, text0, text1, current, dispose; - window.addEventListener("scroll", ctx.onwindowscroll); add_render_callback(ctx.onwindowscroll); - component.$on("state", ({ changed, current }) => { - if (changed["y"] && !window_updating) { - window_updating = true; - clearTimeout(window_updating_timeout); - window.scrollTo(window.pageXOffset, current["y"]); - window_updating_timeout = setTimeout(clear_window_updating, 100); - } - }); - return { c() { p = createElement("p"); text0 = createText("scrolled to "); text1 = createText(ctx.y); + dispose = addListener(window, "scroll", () => { + scrolling = true; + clearTimeout(scrolling_timeout); + scrolling_timeout = setTimeout(clear_scrolling, 100); + ctx.onwindowscroll(); + }); }, m(target, anchor) { @@ -31,6 +27,13 @@ function create_fragment(component, ctx) { }, p(changed, ctx) { + if (changed.y && !scrolling) { + scrolling = true; + clearTimeout(scrolling_timeout); + window.scrollTo(window.pageXOffset, current["y"]); + scrolling_timeout = setTimeout(clear_scrolling, 100); + } + if (changed.y) { setData(text1, ctx.y); } @@ -44,11 +47,11 @@ function create_fragment(component, ctx) { o: run, d(detach) { - window.removeEventListener("scroll", ctx.onwindowscroll); - if (detach) { detachNode(p); } + + dispose(); } }; } @@ -57,10 +60,7 @@ function define($$self, $$props, $$make_dirty) { let { y } = $$props; function onwindowscroll() { - if (window_updating) return; - window_updating = true; y = window.pageYOffset; $$make_dirty('y'); - window_updating = false; } $$self.$$.get = () => ({ y, onwindowscroll });