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.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="media-asset-component media-asset-component-fit-height media-asset-component-fit-width flex flex-col <%= outer_classes %>" data-dynamic-height="<%= dynamic_height %>" style="--header-visible-height: 0px; --media-asset-width: <%= image_width %>; --media-asset-height: <%= image_height %>;">
|
||||
<div class="media-asset-component media-asset-component-fit-height media-asset-component-fit-width flex flex-col <%= outer_classes %>" data-dynamic-height="<%= dynamic_height %>" data-scroll-on-zoom="<%= scroll_on_zoom %>" style="--header-visible-height: 0px; --media-asset-width: <%= image_width %>; --media-asset-height: <%= image_height %>;">
|
||||
<%= header %>
|
||||
|
||||
<div class="media-asset-container relative max-h-inherit overflow-hidden <%= inner_classes %>">
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div id="a-show">
|
||||
<div class="md:flex flex-row gap-4">
|
||||
<div class="flex-1 w-full min-w-0">
|
||||
<%= 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 %>
|
||||
<div class="paginator top-0 w-full z-10">
|
||||
<%= 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" %>
|
||||
|
||||
Reference in New Issue
Block a user