From 16c9e906b235f4fcb220b9b228562bbb9613f6b9 Mon Sep 17 00:00:00 2001 From: evazion Date: Fri, 9 Dec 2022 18:18:42 -0600 Subject: [PATCH] media assets: scroll to position on zoom. On the media asset show page, make it so that when you click an image to zoom in, the page scrolls to the clicked position. For example, if you click on the bottom of an image, scroll down so that the bottom of the zoomed image is in view. This makes it easier to view very tall images by letting you click on the part of the image you want to see. --- app/components/media_asset_component.rb | 5 +++-- .../media_asset_component.html.erb | 2 +- .../src/javascripts/media_asset_component.js | 22 ++++++++++++++++--- app/views/media_assets/show.html.erb | 2 +- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/app/components/media_asset_component.rb b/app/components/media_asset_component.rb index 2fdc77be0..6f07d1b35 100644 --- a/app/components/media_asset_component.rb +++ b/app/components/media_asset_component.rb @@ -2,19 +2,20 @@ # A component for showing a full-sized image or video for a media asset. class MediaAssetComponent < ApplicationComponent - attr_reader :media_asset, :current_user, :outer_classes, :inner_classes, :dynamic_height + attr_reader :media_asset, :current_user, :outer_classes, :inner_classes, :dynamic_height, :scroll_on_zoom delegate :image_width, :image_height, :variant, :is_image?, :is_video?, :is_ugoira?, :is_flash?, to: :media_asset renders_one :header renders_one :footer - def initialize(media_asset:, current_user:, outer_classes: "", inner_classes: "", dynamic_height: false) + def initialize(media_asset:, current_user:, outer_classes: "", inner_classes: "", dynamic_height: false, scroll_on_zoom: false) super @media_asset = media_asset @current_user = current_user @outer_classes = outer_classes @inner_classes = inner_classes @dynamic_height = dynamic_height + @scroll_on_zoom = scroll_on_zoom end end diff --git a/app/components/media_asset_component/media_asset_component.html.erb b/app/components/media_asset_component/media_asset_component.html.erb index 949efb4a2..315e36396 100644 --- a/app/components/media_asset_component/media_asset_component.html.erb +++ b/app/components/media_asset_component/media_asset_component.html.erb @@ -1,4 +1,4 @@ -
+
<%= header %>
diff --git a/app/javascript/src/javascripts/media_asset_component.js b/app/javascript/src/javascripts/media_asset_component.js index 1fab9d4e9..e631f7af8 100644 --- a/app/javascript/src/javascripts/media_asset_component.js +++ b/app/javascript/src/javascripts/media_asset_component.js @@ -1,3 +1,5 @@ +import clamp from "lodash/clamp"; + export default class MediaAssetComponent { static initialize() { $(".media-asset-component").toArray().forEach(element => { @@ -10,14 +12,16 @@ export default class MediaAssetComponent { this.$container = this.$component.find(".media-asset-container"); this.$image = this.$component.find(".media-asset-image"); this.$zoomLevel = this.$component.find(".media-asset-zoom-level"); + this.scrollOnZoom = this.$component.attr("data-scroll-on-zoom") === "true"; + this.dynamicHeight = this.$component.attr("data-dynamic-height") === "true"; - if (this.$component.attr("data-dynamic-height") === "true") { + if (this.dynamicHeight) { this.updateHeight(); $(window).on("scroll.danbooru", e => this.updateHeight()); } if (this.$image.length) { - this.$image.on("click.danbooru", e => this.toggleFit()); + this.$image.on("click.danbooru", e => this.toggleFit(e)); this.$image.on("load.danbooru", e => this.updateZoom()); this.$image.on("load.danbooru", e => this.updateHeight()); new ResizeObserver(() => this.updateZoom()).observe(this.$image.get(0)); @@ -25,13 +29,25 @@ export default class MediaAssetComponent { } } - toggleFit() { + toggleFit(e) { + let rect = this.$image.get(0).getBoundingClientRect(); + let yRatio = (e.clientY - rect.top) / this.$image.height(); + let gap = this.$component.height() - this.$image.height(); + if (this.canZoomOut) { this.$component.addClass("media-asset-component-fit-height media-asset-component-fit-width"); } else if (this.canZoomHeight) { this.$component.removeClass("media-asset-component-fit-height"); } + let top = this.$component.offset().top; + let height = this.$image.height(); + let ypos = clamp(top + yRatio * height - (window.innerHeight / 2), Math.min(window.scrollY, top), top + height + gap - window.innerHeight); + + if (this.scrollOnZoom) { + window.scrollTo({ top: ypos }); + } + this.updateZoom(); } diff --git a/app/views/media_assets/show.html.erb b/app/views/media_assets/show.html.erb index c0d5508c7..76d5e1424 100644 --- a/app/views/media_assets/show.html.erb +++ b/app/views/media_assets/show.html.erb @@ -2,7 +2,7 @@
- <%= render MediaAssetComponent.new(media_asset: @media_asset, current_user: CurrentUser.user, outer_classes: "sticky h-full relative top-0", inner_classes: "mx-auto", dynamic_height: true) do |component| %> + <%= render MediaAssetComponent.new(media_asset: @media_asset, current_user: CurrentUser.user, outer_classes: "sticky h-full relative top-0", inner_classes: "mx-auto", dynamic_height: true, scroll_on_zoom: true) do |component| %> <% component.with_header do %>
<%= link_to chevron_left_icon, media_assets_path(search: { id: ">#{@media_asset.id}", order: "id_asc" }, limit: 1, redirect: true), class: "paginator-prev flex items-center justify-center text-xl absolute left-0 z-10", "data-shortcut": "a left" %>