From 8fed12749c1034886fcfcfcdeb3f0f6950751598 Mon Sep 17 00:00:00 2001 From: snewcomer Date: Tue, 17 Sep 2019 22:15:42 -0700 Subject: [PATCH 1/6] Lazy Load Artwork example --- assets/css/app.css | 4 ++ assets/js/app.js | 43 ++++++++++++++++ assets/package-lock.json | 46 +++++++++++++----- assets/package.json | 1 + assets/static/images/1x1.gif | Bin 0 -> 43 bytes lib/demo/accounts/user.ex | 3 +- .../live/user_live/index_auto_scroll.ex | 15 +++++- .../live/user_live/index_manual_scroll.ex | 15 +++++- .../20190918041148_add_artwork_to_user.exs | 9 ++++ priv/repo/seeds.exs | 14 ++++++ 10 files changed, 136 insertions(+), 14 deletions(-) create mode 100644 assets/static/images/1x1.gif create mode 100644 priv/repo/migrations/20190918041148_add_artwork_to_user.exs diff --git a/assets/css/app.css b/assets/css/app.css index 093dee6..84b22ec 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -75,6 +75,10 @@ pre{ animation: slide-up 0.4s ease; } +.user-artwork { + border-radius: 50%; +} + @keyframes slide-up { 0% { opacity: 0; diff --git a/assets/js/app.js b/assets/js/app.js index ce60a45..1254d6f 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -2,6 +2,7 @@ import css from "../css/app.css"; import "phoenix_html" import {Socket} from "phoenix" import {LiveSocket, debug, View} from "phoenix_live_view" +import IntersectionObserverAdmin from 'intersection-observer-admin'; let Hooks = {} @@ -39,6 +40,48 @@ Hooks.InfiniteScroll = { updated(){ this.pending = this.page() } } +// TODO: make IE11 compat with rAF +const observerAdmin = new IntersectionObserverAdmin(); +const observerOptions = { rootMargin: '0px 0px 0px 0px' , threshold: 0 }; + +Hooks.LazyArtwork = { + observerAdmin, + artwork() { return this.el.querySelector('img') }, + + mounted() { + window.requestIdleCallback(() => { + let enterCallback = ({ target: img }) => { + if (img) { + if (img && img.dataset) { + img.src = img.dataset.src; + } + } + } + + let exitCallback = ({ isIntersecting, target: img }) => { + if (isIntersecting) { + this.observerAdmin.unobserve(img, observerOptions); + } + } + + const artwork = this.artwork(); + this.observerAdmin.addEnterCallback( + artwork, + enterCallback + ) + this.observerAdmin.addExitCallback( + artwork, + exitCallback + ) + + this.observerAdmin.observe( + artwork, + observerOptions + ) + }); + } +} + let serializeForm = (form) => { let formData = new FormData(form) let params = new URLSearchParams() diff --git a/assets/package-lock.json b/assets/package-lock.json index 31ab01f..a04e963 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -2877,7 +2877,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2898,12 +2899,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2918,17 +2921,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -3045,7 +3051,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -3057,6 +3064,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3071,6 +3079,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3078,12 +3087,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -3102,6 +3113,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -3182,7 +3194,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3194,6 +3207,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3279,7 +3293,8 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3315,6 +3330,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3334,6 +3350,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3377,12 +3394,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -4050,6 +4069,11 @@ "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", "dev": true }, + "intersection-observer-admin": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/intersection-observer-admin/-/intersection-observer-admin-0.2.6.tgz", + "integrity": "sha512-9EJj31Zrc0y3sBS8Fcv+7h1JaeoNtevs8bvS0jbWy1IkY4uhRoAQOBDl3iPKfXY/sz6N731z5o6mIZRwRjvr4Q==" + }, "into-stream": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", diff --git a/assets/package.json b/assets/package.json index ed960f4..2404e73 100644 --- a/assets/package.json +++ b/assets/package.json @@ -6,6 +6,7 @@ "watch": "webpack --mode development --watch" }, "dependencies": { + "intersection-observer-admin": "^0.2.6", "phoenix": "../deps/phoenix", "phoenix_html": "file:../deps/phoenix_html", "phoenix_live_view": "file:../deps/phoenix_live_view" diff --git a/assets/static/images/1x1.gif b/assets/static/images/1x1.gif new file mode 100644 index 0000000000000000000000000000000000000000..9d76ec6b19826cee1df4b2908830798d1ec93323 GIT binary patch literal 43 qcmZ?wbh9u|WMp7u_`m=HYOW_0f3h$#00neFe2@$S6O#`kgEau2`UR%| literal 0 HcmV?d00001 diff --git a/lib/demo/accounts/user.ex b/lib/demo/accounts/user.ex index 1c8c941..bc56070 100644 --- a/lib/demo/accounts/user.ex +++ b/lib/demo/accounts/user.ex @@ -3,6 +3,7 @@ defmodule Demo.Accounts.User do import Ecto.Changeset schema "users" do + field :artwork, :map field :username, :string field :email, :string field :phone_number, :string @@ -17,7 +18,7 @@ defmodule Demo.Accounts.User do @doc false def changeset(user, attrs) do user - |> cast(attrs, [:username, :email, :phone_number, :password]) + |> cast(attrs, [:username, :artwork, :email, :phone_number, :password]) |> validate_required([:username, :email, :phone_number]) |> validate_confirmation(:password) |> validate_format(:username, ~r/^[a-zA-Z0-9_]*$/, diff --git a/lib/demo_web/live/user_live/index_auto_scroll.ex b/lib/demo_web/live/user_live/index_auto_scroll.ex index c41c3b0..5063cb3 100644 --- a/lib/demo_web/live/user_live/index_auto_scroll.ex +++ b/lib/demo_web/live/user_live/index_auto_scroll.ex @@ -10,6 +10,19 @@ defmodule DemoWeb.UserLive.IndexAutoScroll do data-page="<%= @page %>"> <%= for user <- @users do %> + + ; + data-src=<%= Map.get(user.artwork, "url") %> + alt=<%= user.username %> + height=<%= Map.get(user.artwork, "height") %> + width=<%= Map.get(user.artwork, "width") %> + style="background-color: lightgray" + role="presentation" + data-lazy-artwork + /> + <%= user.username %> <%= user.email %> @@ -22,7 +35,7 @@ defmodule DemoWeb.UserLive.IndexAutoScroll do def mount(_session, socket) do {:ok, socket - |> assign(page: 1, per_page: 10) + |> assign(page: 1, per_page: 20) |> fetch(), temporary_assigns: [:users]} end diff --git a/lib/demo_web/live/user_live/index_manual_scroll.ex b/lib/demo_web/live/user_live/index_manual_scroll.ex index cd48025..5240846 100644 --- a/lib/demo_web/live/user_live/index_manual_scroll.ex +++ b/lib/demo_web/live/user_live/index_manual_scroll.ex @@ -7,6 +7,19 @@ defmodule DemoWeb.UserLive.IndexManualScroll do <%= for user <- @users do %> + + ; + data-src=<%= Map.get(user.artwork, "url") %> + alt=<%= user.username %> + height=<%= Map.get(user.artwork, "height") %> + width=<%= Map.get(user.artwork, "width") %> + style="background-color: lightgray" + role="presentation" + data-lazy-artwork + /> + <%= user.username %> <%= user.email %> @@ -22,7 +35,7 @@ defmodule DemoWeb.UserLive.IndexManualScroll do def mount(_session, socket) do {:ok, socket - |> assign(page: 1, per_page: 10, val: 0) + |> assign(page: 1, per_page: 20, val: 0) |> fetch(), temporary_assigns: [:users]} end diff --git a/priv/repo/migrations/20190918041148_add_artwork_to_user.exs b/priv/repo/migrations/20190918041148_add_artwork_to_user.exs new file mode 100644 index 0000000..c468a82 --- /dev/null +++ b/priv/repo/migrations/20190918041148_add_artwork_to_user.exs @@ -0,0 +1,9 @@ +defmodule Demo.Repo.Migrations.AddArtworkToUser do + use Ecto.Migration + + def change do + alter table(:users) do + add(:artwork, :map) + end + end +end diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 22d419d..802bfae 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -10,11 +10,25 @@ # We recommend using the bang functions (`insert!`, `update!` # and so on) as they will fail if something goes wrong. + for i <- 1..1000 do + artworks = [ + "https://s3.amazonaws.com/uifaces/faces/twitter/jarjan/128.jpg", + "https://s3.amazonaws.com/uifaces/faces/twitter/aio___/128.jpg", + "https://s3.amazonaws.com/uifaces/faces/twitter/kolage/128.jpg", + "https://s3.amazonaws.com/uifaces/faces/twitter/sauro/128.jpg", + "https://s3.amazonaws.com/uifaces/faces/twitter/jina/128.jpg" + ] + {:ok, _} = Demo.Accounts.create_user(%{ username: "user#{i}", name: "User #{i}", + artwork: %{ + url: Enum.take_random(artworks, 1) |> Enum.at(0), + height: 65, + width: 65 + }, email: "user#{i}@test", phone_number: "555-555-5555" }) From 758fb601712921c097a279439bbe58f6e65507de Mon Sep 17 00:00:00 2001 From: snewcomer Date: Wed, 18 Sep 2019 20:38:29 -0700 Subject: [PATCH 2/6] add phx ignore --- assets/package-lock.json | 3 ++- assets/package.json | 2 +- lib/demo_web/live/user_live/index_auto_scroll.ex | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/assets/package-lock.json b/assets/package-lock.json index a04e963..986130f 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -4072,7 +4072,8 @@ "intersection-observer-admin": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/intersection-observer-admin/-/intersection-observer-admin-0.2.6.tgz", - "integrity": "sha512-9EJj31Zrc0y3sBS8Fcv+7h1JaeoNtevs8bvS0jbWy1IkY4uhRoAQOBDl3iPKfXY/sz6N731z5o6mIZRwRjvr4Q==" + "integrity": "sha512-9EJj31Zrc0y3sBS8Fcv+7h1JaeoNtevs8bvS0jbWy1IkY4uhRoAQOBDl3iPKfXY/sz6N731z5o6mIZRwRjvr4Q==", + "dev": true }, "into-stream": { "version": "3.1.0", diff --git a/assets/package.json b/assets/package.json index 2404e73..e037a3a 100644 --- a/assets/package.json +++ b/assets/package.json @@ -6,7 +6,6 @@ "watch": "webpack --mode development --watch" }, "dependencies": { - "intersection-observer-admin": "^0.2.6", "phoenix": "../deps/phoenix", "phoenix_html": "file:../deps/phoenix_html", "phoenix_live_view": "file:../deps/phoenix_live_view" @@ -18,6 +17,7 @@ "copy-webpack-plugin": "^4.5.0", "css-loader": "^0.28.10", "extract-text-webpack-plugin": "^4.0.0-beta.0", + "intersection-observer-admin": "^0.2.6", "style-loader": "^0.20.2", "webpack": "4.0.0", "webpack-cli": "^2.0.10" diff --git a/lib/demo_web/live/user_live/index_auto_scroll.ex b/lib/demo_web/live/user_live/index_auto_scroll.ex index 5063cb3..bbfb7a3 100644 --- a/lib/demo_web/live/user_live/index_auto_scroll.ex +++ b/lib/demo_web/live/user_live/index_auto_scroll.ex @@ -20,6 +20,7 @@ defmodule DemoWeb.UserLive.IndexAutoScroll do width=<%= Map.get(user.artwork, "width") %> style="background-color: lightgray" role="presentation" + phx-update="ignore" data-lazy-artwork /> From 7b86252b9c59cd663d183281b43893940f195d4b Mon Sep 17 00:00:00 2001 From: snewcomer Date: Thu, 19 Sep 2019 10:18:37 -0700 Subject: [PATCH 3/6] Sentinel with observer --- assets/js/app.js | 40 ++++++++++++++++++- .../live/user_live/index_auto_scroll.ex | 7 ++-- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index 1254d6f..0c8bc98 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -42,7 +42,45 @@ Hooks.InfiniteScroll = { // TODO: make IE11 compat with rAF const observerAdmin = new IntersectionObserverAdmin(); -const observerOptions = { rootMargin: '0px 0px 0px 0px' , threshold: 0 }; +const sentinelOptions = { rootMargin: '0px 0px 90px 0px', threshold: 0 }; +const observerOptions = { rootMargin: '0px 0px 0px 0px', threshold: 0 }; + +Hooks.ObserverInfiniteScroll = { + observerAdmin, + page() { return this.el.dataset.page }, + mounted(){ + this.pending = this.page() + let enterCallback = ({ target }) => { + if (this.pending == this.page()) { + this.pending = this.page() + 1 + this.pushEvent("load-more", {}) + } + } + + let exitCallback = ({ isIntersecting, target }) => { + if (isIntersecting) { + this.observerAdmin.unobserve(target, sentinelOptions); + } + } + + this.observerAdmin.addEnterCallback( + this.el, + enterCallback.bind(this) + ) + this.observerAdmin.addExitCallback( + this.el, + exitCallback.bind(this) + ) + + this.observerAdmin.observe( + this.el, + sentinelOptions + ) + }, + + // after DOM Patch + updated(){ this.pending = this.page() } +} Hooks.LazyArtwork = { observerAdmin, diff --git a/lib/demo_web/live/user_live/index_auto_scroll.ex b/lib/demo_web/live/user_live/index_auto_scroll.ex index bbfb7a3..452f132 100644 --- a/lib/demo_web/live/user_live/index_auto_scroll.ex +++ b/lib/demo_web/live/user_live/index_auto_scroll.ex @@ -4,10 +4,7 @@ defmodule DemoWeb.UserLive.IndexAutoScroll do def render(assigns) do ~L""" - + <%= for user <- @users do %>
@@ -30,6 +27,8 @@ defmodule DemoWeb.UserLive.IndexAutoScroll do <% end %>
+ +
""" end From 044e18c9c037cc54f7a4783c606c52788a4d65b4 Mon Sep 17 00:00:00 2001 From: snewcomer Date: Wed, 27 Nov 2019 17:38:07 -0700 Subject: [PATCH 4/6] bump --- assets/package-lock.json | 6 +++--- assets/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/package-lock.json b/assets/package-lock.json index 986130f..3b85f52 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -4070,9 +4070,9 @@ "dev": true }, "intersection-observer-admin": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/intersection-observer-admin/-/intersection-observer-admin-0.2.6.tgz", - "integrity": "sha512-9EJj31Zrc0y3sBS8Fcv+7h1JaeoNtevs8bvS0jbWy1IkY4uhRoAQOBDl3iPKfXY/sz6N731z5o6mIZRwRjvr4Q==", + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/intersection-observer-admin/-/intersection-observer-admin-0.2.9.tgz", + "integrity": "sha512-lpZoEMa9VrHWnpy0uRFqVvPXL+mZ8vYPpOMPC9H64kAF7f+256gCjrwvIl/MjCIobrblAD3VVxLoiUqIKIfYTw==", "dev": true }, "into-stream": { diff --git a/assets/package.json b/assets/package.json index e037a3a..fce90f8 100644 --- a/assets/package.json +++ b/assets/package.json @@ -17,7 +17,7 @@ "copy-webpack-plugin": "^4.5.0", "css-loader": "^0.28.10", "extract-text-webpack-plugin": "^4.0.0-beta.0", - "intersection-observer-admin": "^0.2.6", + "intersection-observer-admin": "^0.2.9", "style-loader": "^0.20.2", "webpack": "4.0.0", "webpack-cli": "^2.0.10" From 6fdf05c865578571a589a30d492a095193f1d4c6 Mon Sep 17 00:00:00 2001 From: snewcomer Date: Fri, 31 Jan 2020 13:17:44 -0800 Subject: [PATCH 5/6] bring up to date --- lib/demo_web/live/user_live/index_auto_scroll.ex | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/demo_web/live/user_live/index_auto_scroll.ex b/lib/demo_web/live/user_live/index_auto_scroll.ex index 973f968..b580cf2 100644 --- a/lib/demo_web/live/user_live/index_auto_scroll.ex +++ b/lib/demo_web/live/user_live/index_auto_scroll.ex @@ -10,7 +10,7 @@ defmodule DemoWeb.UserLive.Row do def render(assigns) do ~L""" - + Email: <%= @email %> <%= @count %> """ @@ -28,6 +28,20 @@ defmodule DemoWeb.UserLive.Row do def render(assigns) do ~L""" + + ; + data-src=<%= Map.get(@user.artwork, "url") %> + alt=<%= @user.username %> + height=<%= Map.get(@user.artwork, "height") %> + width=<%= Map.get(@user.artwork, "width") %> + style="background-color: lightgray" + role="presentation" + phx-update="ignore" + data-lazy-artwork + /> + <%= @user.username %> <%= @count %> <%= live_component @socket, Email, id: "email-#{@id}", email: @user.email %> From a2928ac3a765f6fb9c9325d96adbca1baae331fb Mon Sep 17 00:00:00 2001 From: snewcomer Date: Fri, 31 Jan 2020 13:18:43 -0800 Subject: [PATCH 6/6] bump lock --- assets/package-lock.json | 6 +++--- assets/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/package-lock.json b/assets/package-lock.json index d3cc8db..b32d7ba 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -5285,9 +5285,9 @@ "dev": true }, "intersection-observer-admin": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/intersection-observer-admin/-/intersection-observer-admin-0.2.9.tgz", - "integrity": "sha512-lpZoEMa9VrHWnpy0uRFqVvPXL+mZ8vYPpOMPC9H64kAF7f+256gCjrwvIl/MjCIobrblAD3VVxLoiUqIKIfYTw==", + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/intersection-observer-admin/-/intersection-observer-admin-0.2.10.tgz", + "integrity": "sha512-qQo6SDLSSJYMDYo46b9rDA47VFiCn4pOhEMb97I4+iuFJBRfB28+nWpFtlAfkMwUyST30YNlovtebQOwsGKX1g==", "dev": true }, "into-stream": { diff --git a/assets/package.json b/assets/package.json index e6e310b..56e117f 100644 --- a/assets/package.json +++ b/assets/package.json @@ -22,7 +22,7 @@ "copy-webpack-plugin": "^4.5.0", "css-loader": "^0.28.10", "extract-text-webpack-plugin": "^4.0.0-beta.0", - "intersection-observer-admin": "^0.2.9", + "intersection-observer-admin": "^0.2.10", "morphdom": "^2.5.12", "style-loader": "^0.20.2", "webpack": "4.0.0",