You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Affine Transformation旋转图像时超出可视边界的解决方法

问题描述

我正在使用Affine Transformation旋转图像实现摆锤来回摆动的效果,但图像向左旋转时会超出可视边界消失。怎么调整边界让图像始终可见?

摆动的图像:
摆动的摆锤图像

旋转图像的代码:

public void paintComponent(Graphics g) {
    // if no image set, then just draw a rectangle
    if (sprite == null) {
        g.setColor(c);
        g.fillRect(0, 0, (int) getBounds().getWidth(), (int) getBounds().getHeight());
    } else {
        try {
            AffineTransform at = new AffineTransform();
            if (spriteAngle != 0) {
                at.rotate(spriteAngle, getWidth() / 2, getHeight() / 2);
            }
            at.scale(getWidth() * 1.0 / sprite.getWidth(), getHeight() * 1.0 / sprite.getHeight());
            Graphics2D g2d = (Graphics2D) g;
            g2d.drawImage(sprite, at, null);
        } catch (Exception e) {
            g.drawImage(sprite, 0, 0, getWidth(), getHeight(), this);
        }
    }
}

我试过用Rectangle类做临时变换移动边界、平移对象,也试过绕中心旋转、更换图像。把图像放在右下角能让它始终在边界内:
右下角放置的图像
但这个方法不行,因为我需要随时间延长图像长度。

解决方案

要解决旋转时图像出界的问题,核心是让旋转后的图像始终落在组件可视区域内,从这几个方面调整:

1. 换旋转原点,对齐摆锤固定端

当前代码绕组件中心旋转,会导致图像两端超出边界。摆锤应该绕自身的固定端点(比如图像顶部中心)旋转,这样旋转时图像只会在固定点周围摆动,不会轻易出界。

修改代码步骤:

  • 确定图像的旋转原点:比如图像的顶部中心(sprite.getWidth()/2, 0)。
  • 先平移图像,让这个旋转原点对齐到组件内的固定位置(比如组件顶部中心)。
  • 再执行旋转操作,最后绘制图像。

修改后的代码示例:

public void paintComponent(Graphics g) {
    if (sprite == null) {
        g.setColor(c);
        g.fillRect(0, 0, getWidth(), getHeight());
    } else {
        Graphics2D g2d = (Graphics2D) g;
        try {
            AffineTransform at = new AffineTransform();
            
            // 等比例缩放,避免图像拉伸变形
            double scale = Math.min((double) getWidth() / sprite.getWidth(), (double) getHeight() / sprite.getHeight());
            at.scale(scale, scale);
            
            // 图像的旋转原点:顶部中心(摆锤固定端)
            int imgRotX = sprite.getWidth() / 2;
            int imgRotY = 0;
            
            // 平移图像,让旋转原点对齐到组件顶部中心
            double compRotX = getWidth() / 2.0 / scale; // 转换到缩放后的坐标
            at.translate(compRotX - imgRotX, 0 - imgRotY);
            
            // 绕图像顶部中心旋转
            if (spriteAngle != 0) {
                at.rotate(spriteAngle, imgRotX, imgRotY);
            }
            
            g2d.drawImage(sprite, at, null);
        } catch (Exception e) {
            g.drawImage(sprite, 0, 0, getWidth(), getHeight(), this);
        }
    }
}

2. 让组件尺寸适配旋转后的图像

旋转后的图像外接矩形会比原图像大,需要让组件尺寸能容纳这个外接矩形。重写getPreferredSize()方法,返回计算后的最小尺寸:

private Dimension getRotatedImageSize() {
    if (sprite == null) return new Dimension(100, 100);
    double angle = Math.abs(spriteAngle);
    int imgW = sprite.getWidth();
    int imgH = sprite.getHeight();
    
    // 计算旋转后外接矩形的宽高
    double rotatedW = Math.abs(imgW * Math.cos(angle)) + Math.abs(imgH * Math.sin(angle));
    double rotatedH = Math.abs(imgW * Math.sin(angle)) + Math.abs(imgH * Math.cos(angle));
    
    // 应用缩放比例
    double scale = Math.min((double) getWidth() / imgW, (double) getHeight() / imgH);
    return new Dimension((int)(rotatedW * scale), (int)(rotatedH * scale));
}

@Override
public Dimension getPreferredSize() {
    return getRotatedImageSize();
}

3. 保持图像等比例缩放

原代码直接按组件宽高缩放会导致图像拉伸,用等比例缩放既能保证图像不变形,也能更准确计算旋转后的边界。

内容的提问来源于stack exchange,提问作者341069300

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 07:19:58