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" %>