diff --git a/src/main/java/top/fpsmaster/ui/click/MainPanel.java b/src/main/java/top/fpsmaster/ui/click/MainPanel.java index d03801b1..41961d1f 100644 --- a/src/main/java/top/fpsmaster/ui/click/MainPanel.java +++ b/src/main/java/top/fpsmaster/ui/click/MainPanel.java @@ -144,9 +144,9 @@ public void render(int mouseX, int mouseY, float partialTicks) { float centerX = guiWidth / 2f; float centerY = guiHeight / 2f; float scissorX = centerX + (x - centerX) * scale; - float scissorY = centerY + (y + 10 - centerY) * scale; + float scissorY = centerY + (y - centerY + 10) * scale; float scissorW = width * scale; - float scissorH = (height - 18) * scale; + float scissorH = (height - 12) * scale; GL11.glEnable(GL11.GL_SCISSOR_TEST); Scissor.apply( diff --git a/src/main/java/top/fpsmaster/ui/click/component/ScrollContainer.java b/src/main/java/top/fpsmaster/ui/click/component/ScrollContainer.java index e230e50d..721efee8 100644 --- a/src/main/java/top/fpsmaster/ui/click/component/ScrollContainer.java +++ b/src/main/java/top/fpsmaster/ui/click/component/ScrollContainer.java @@ -2,19 +2,25 @@ import lombok.Getter; import lombok.Setter; -import top.fpsmaster.utils.render.draw.Hover; -import top.fpsmaster.utils.render.draw.Rects; - import top.fpsmaster.utils.math.anim.AnimClock; import top.fpsmaster.utils.math.anim.Animator; import top.fpsmaster.utils.math.anim.Easings; +import top.fpsmaster.utils.render.draw.Hover; +import top.fpsmaster.utils.render.draw.Rects; import top.fpsmaster.utils.render.gui.ScaledGuiScreen; import java.awt.*; public class ScrollContainer { - private float wheel = 0f; // 带动画的位置 - private float wheel_anim = 0f; // 真实滚动到的位置 + private static final float WHEEL_STEP = 50f; + private static final float MAX_OVERSCROLL = 45f; + private static final float OVERSCROLL_RESISTANCE = 0.38f; + private static final long SNAP_BACK_DELAY_NS = 50_000_000L; + private static final float TRACK_MIN_HEIGHT = 18f; + private static final float TRACK_SHRINK_FACTOR = 0.45f; + + private float wheel = 0f; + private float wheelAnim = 0f; @Getter @Setter private float height = 0f; @@ -26,38 +32,48 @@ public class ScrollContainer { private float scrollStart = 0f; private boolean isScrolling = false; private final String captureId = getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(this)); + private long wheelDelay = 0L; public void draw(ScaledGuiScreen screen, float x, float y, float width, float height, int mouseX, int mouseY, Runnable runnable) { double dt = animClock.tick(); runnable.run(); - // if the scroll bar needs to be render + float maxUp = Math.max(0f, this.height - height); + float minScroll = -maxUp; + if (this.height > height) { - // calc scroll bar height - float percent = (height / this.height); - float sHeight = percent * height; - float scrollPercent = (getScroll() / (this.height - height)); - float sY = y - scrollPercent * (height - sHeight); - float sX = x + width + 1 - (float) scrollExpand; + float percent = height / this.height; + float baseHeight = Math.max(TRACK_MIN_HEIGHT, percent * height); + float visibleScroll = clamp(wheel, minScroll, 0f); + float scrollPercent = maxUp <= 0f ? 0f : visibleScroll / maxUp; + float trackHeight = baseHeight; + float trackY = y - scrollPercent * (height - baseHeight); + + float overscrollTop = Math.max(0f, wheel); + float overscrollBottom = Math.max(0f, minScroll - wheel); + if (overscrollTop > 0f) { + trackHeight = shrinkTrack(baseHeight, overscrollTop); + trackY = y; + } else if (overscrollBottom > 0f) { + trackHeight = shrinkTrack(baseHeight, overscrollBottom); + trackY = y + height - trackHeight; + } + + float trackX = x + width + 1 - (float) scrollExpand; Rects.rounded( - Math.round(sX), - Math.round(sY), - Math.round(1f + (float) scrollExpand), - Math.round(sHeight), - 1, - new Color(255, 255, 255, 100).getRGB() + Math.round(trackX), + Math.round(trackY), + Math.round(1f + (float) scrollExpand), + Math.round(trackHeight), + 1, + new Color(255, 255, 255, 100).getRGB() ); - if (Hover.is( - sX - 1, - sY, - 2f + (float) scrollExpand, - sHeight, mouseX, mouseY - ) - ) { + + if (Hover.is(trackX - 1, trackY, 2f + (float) scrollExpand, trackHeight, mouseX, mouseY)) { scrollExpand = 1.0; - if (screen.beginPointerCapture(captureId, 0, sX - 1, sY, 2f + (float) scrollExpand, sHeight)) { + if (screen.beginPointerCapture(captureId, 0, trackX - 1, trackY, 2f + (float) scrollExpand, trackHeight)) { isScrolling = true; - scrollStart = mouseY - sY; + scrollStart = mouseY - trackY; } } else if (!isScrolling) { scrollExpand = 0.0; @@ -65,46 +81,68 @@ public void draw(ScaledGuiScreen screen, float x, float y, float width, float he if (isScrolling) { if (screen.isPointerCapturedBy(captureId, 0)) { - wheel_anim = -((mouseY - scrollStart - y) / height) * this.height; + float dragScroll = -((mouseY - scrollStart - y) / height) * this.height; + wheelAnim = applyOverscrollResistance(dragScroll, minScroll, 0f); + wheelDelay = System.nanoTime(); } else { isScrolling = false; screen.releasePointerCapture(captureId); } } } else { - wheel_anim = 0f; + wheelAnim = 0f; } - if (Hover.is(x,y,width,height, mouseX, mouseY)) { - if (this.height > height) { - int mouseDWheel = screen.consumeWheelDelta(x, y, width, height); + if (Hover.is(x, y, width, height, mouseX, mouseY) && this.height > height) { + int mouseDWheel = screen.consumeWheelDelta(x, y, width, height); + if (mouseDWheel != 0) { + wheelDelay = System.nanoTime(); if (mouseDWheel > 0) { - wheel_anim += 20f; - } else if (mouseDWheel < 0) { - wheel_anim -= 20f; + wheelAnim = applyOverscrollResistance(wheelAnim + WHEEL_STEP, minScroll, 0f); + } else { + wheelAnim = applyOverscrollResistance(wheelAnim - WHEEL_STEP, minScroll, 0f); } - } } - float maxUp = this.height - height; - wheel_anim = Math.min(Math.max(wheel_anim, -maxUp), 0f); - if (Float.isNaN(lastTarget) || wheel_anim != lastTarget) { - wheelAnimator.animateTo(wheel_anim, 0.12f, Easings.QUAD_OUT); - lastTarget = wheel_anim; + + if (Float.isNaN(lastTarget) || wheelAnim != lastTarget) { + wheelAnimator.animateTo(wheelAnim, 0.12f, Easings.CUBIC_OUT); + lastTarget = wheelAnim; + } else if (System.nanoTime() - wheelDelay > SNAP_BACK_DELAY_NS) { + float bounded = clamp(wheelAnim, minScroll, 0f); + if (bounded != wheelAnim) { + wheelAnim = bounded; + } } + wheelAnimator.update(dt); wheel = (float) wheelAnimator.get(); } public int getScroll() { - return (int)wheel; + return (int) wheel; } public float getRealScroll() { - return wheel_anim; + return wheelAnim; } -} - + private float applyOverscrollResistance(float value, float min, float max) { + if (value > max) { + return Math.min(max + MAX_OVERSCROLL, max + (value - max) * OVERSCROLL_RESISTANCE); + } + if (value < min) { + return Math.max(min - MAX_OVERSCROLL, min + (value - min) * OVERSCROLL_RESISTANCE); + } + return value; + } + private float shrinkTrack(float baseHeight, float overscroll) { + float shrink = Math.min(baseHeight - TRACK_MIN_HEIGHT, overscroll * TRACK_SHRINK_FACTOR); + return Math.max(TRACK_MIN_HEIGHT, baseHeight - shrink); + } + private float clamp(float value, float min, float max) { + return Math.max(min, Math.min(max, value)); + } +}