Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
248 views
in Technique[技术] by (71.8m points)

java - How do I make this panel zoom toward the middle of the panel?

I have a JPanel with the code here. I want the panel to zoom in towards the center of what's in the panel when I scroll with the mousewheel. Currently, whenever I zoom in/out with the mouse wheel, the corner in the top left of the image stays in the same place. I've had a difficult time finding a correct algorithm.

To zoom into the picture, the code uses an AffineTransform object which scales the image according to a double value which increases or decreases based on the movement of the mouse wheel.

What also adds to the complexity is that the image can also be clicked and dragged around the panel. If it is clicked and dragged, the zoom must still zoom in on what's in the center of the PANEL, and not the center of the actual image necessarily.

Once again, the zooming should be done relative to the center point of the currently visible area. That is, as zooming occurs, the point at the center of the view should remain fixed.

This is the code (and it's executable):

package clientgui;

import java.awt.*;

import javax.imageio.ImageIO;
import javax.swing.*;

import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.net.URL;

import javax.swing.border.TitledBorder;

public class MoveImageExample extends JFrame {
    ShowCanvas canvas;

    public MoveImageExample() throws Exception {
        super();
        Container container = getContentPane();
        canvas = new ShowCanvas(
                "http://cdn.smosh.com/sites/default/files/bloguploads/funny-iphone-5-bruce-lee.jpg");
        container.setPreferredSize(new Dimension(canvas.getWidth(), canvas
                .getHeight()));
        System.out.println("width = " + canvas.getWidth() + " height = "
                + canvas.getHeight());
        container.add(canvas);
        pack();
        setVisible(true);
    }

    public static void main(String arg[]) throws Exception {
        new MoveImageExample();
    }
}

@SuppressWarnings("serial")
class ShowCanvas extends JPanel {
    int imageX = 0, imageY = 0;
    int lastMouseX = 0, lastMouseY = 0;
    int centerX = 225;
    int centerY = 225;
    int canvasWidth = 450;
    int canvasHeight = 450;
    double scaleFactor = 1.0;
    boolean firstMouseDrag = true;
    BufferedImage image;

    public ShowCanvas(String imagePath) throws Exception {
        setBackground(Color.white);
        MouseMotionHandler mouseHandler = new MouseMotionHandler();
        addMouseMotionListener(mouseHandler);
        addMouseListener(mouseHandler);
        addMouseWheelListener(mouseHandler);
        URL url = new URL(imagePath);
        Image rawImage = ImageIO.read(url);
        image = new BufferedImage(rawImage.getWidth(this),
                rawImage.getHeight(this), BufferedImage.TYPE_INT_ARGB);
        setSize(image.getWidth(), image.getHeight());
        Graphics2D g2 = image.createGraphics();
        g2.drawImage(rawImage, imageX, imageY, this);
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2D = (Graphics2D) g;
        g2D.setColor(Color.gray);
        g.fillRect(0, 0, image.getWidth(), image.getHeight());
        AffineTransform transformer = new AffineTransform();
        // translate the image back (using new scale factor)
        transformer.scale(scaleFactor, scaleFactor); // scale by 2x on x and y
                                                        // axes.
        transformer.translate(imageX / scaleFactor, imageY / scaleFactor);
        g2D.drawImage(image, transformer, this);
    }

    class MouseMotionHandler extends MouseMotionAdapter implements
            MouseListener, MouseWheelListener {
        public void mousePressed(MouseEvent e) {
            lastMouseX = e.getX();
            lastMouseY = e.getY();
        }

        public void mouseDragged(MouseEvent e) {
            int xDiff = e.getX() - lastMouseX;
            int yDiff = e.getY() - lastMouseY;
            imageX = imageX + xDiff;
            imageY = imageY + yDiff;
            lastMouseX = e.getX();
            lastMouseY = e.getY();
            repaint();
        }

        public void mouseWheelMoved(MouseWheelEvent e) {
            int notches = e.getWheelRotation();
            scaleFactor = scaleFactor + notches / 10.0;
            if (scaleFactor < 0.5) {
                scaleFactor = 0.5;
            } else if (scaleFactor > 3.0) {
                scaleFactor = 3.0;
            }
            repaint();
        }

        public void mouseReleased(MouseEvent e) {
        }

        public void mouseEntered(MouseEvent e) {
        }

        public void mouseExited(MouseEvent e) {
        }

        public void mouseClicked(MouseEvent e) {
        }
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

So, the basic idea, is when you change the scale, rather then allowing the entire change to be added/subtracted from the width/height, you need to divide it between the location and the size...

public void mouseWheelMoved(MouseWheelEvent e) {
    int notches = e.getWheelRotation();

    // Get the current/old size...    
    double oldWidth = image.getWidth() * scaleFactor;
    double oldHeight = image.getHeight() * scaleFactor;

    scaleFactor = scaleFactor + notches / 10.0;
    if (scaleFactor < 0.5) {
        scaleFactor = 0.5;
    } else if (scaleFactor > 3.0) {
        scaleFactor = 3.0;
    }
    // Get the new size
    double newWidth = image.getWidth() * scaleFactor;
    double newHeight = image.getHeight() * scaleFactor;

    // Calculate the difference (and divide it by 2)
    double difWidth = (oldWidth - newWidth) / 2;
    double difHeight = (oldHeight - newHeight) / 2;

    // Add it to the image position...
    imageX += difWidth;
    imageY += difHeight;

    revalidate();
    repaint();
}

Updated with working example

Okay, so the basic idea is you're dealing with a virtual space, where the image sits. This virtual space has a size (the component size x the scale factor). This allows you to center the virtual space within the actual space.

After that, you simply need to calculate the x/y offset of the virtual space (within the real space) and the virtual location of the image.

In this example, I removed the AffineTransformation#setLocation in favor of generating a scaled instance of the image, it simply made it easier to place the image.

import java.awt.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.net.URL;

public class MoveImageExample extends JFrame {

    ShowCanvas canvas;

    public MoveImageExample() throws Exception {
        super();
        Container container = getContentPane();
        canvas = new ShowCanvas(
                        "http://cdn.smosh.com/sites/default/files/bloguploads/funny-iphone-5-bruce-lee.jpg");
        container.add(canvas);
        pack();
        setVisible(true);
    }

    public static void main(String arg[]) throws Exception {
        new MoveImageExample();
    }

}

@SuppressWarnings("serial")
final class ShowCanvas extends JPanel {

    int imageX = 0, imageY = 0;
    int lastMouseX = 0, lastMouseY = 0;
    int centerX = 225;
    int centerY = 225;
    int canvasWidth = 450;
    int canvasHeight = 450;
    double scaleFactor = 1.0;
    boolean firstMouseDrag = true;
    BufferedImage image;
    private BufferedImage scaled;

    public ShowCanvas(String imagePath) throws Exception {
        setBackground(Color.white);
        MouseMotionHandler mouseHandler = new MouseMotionHandler();
        addMouseMotionListener(mouseHandler);
        addMouseListener(mouseHandler);
        addMouseWheelListener(mouseHandler);
        URL url = new URL(imagePath);
        Image rawImage = ImageIO.read(url);
        image = new BufferedImage(rawImage.getWidth(this),
                        rawImage.getHeight(this), BufferedImage.TYPE_INT_ARGB);
        setSize(image.getWidth(), image.getHeight());
        Graphics2D g2 = image.createGraphics();
        g2.drawImage(rawImage, imageX, imageY, this);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension((int) (image.getWidth()), (int) (image.getHeight()));
    }

    protected BufferedImage getScaledInstance() {
        if (scaled == null) {
            int width = (int) (image.getWidth() * scaleFactor);
            int height = (int) (image.getHeight() * scaleFactor);
            scaled = new BufferedImage(width, height, image.getType());
            Graphics2D g2d = scaled.createGraphics();
            AffineTransform transformer = new AffineTransform();
            transformer.scale(scaleFactor, scaleFactor); // scale by 2x on x and y
            g2d.setTransform(transformer);
            g2d.drawImage(image, 0, 0, this);
            g2d.dispose();
        }
        return scaled;
    }

    public Dimension getVirtualSize() {
        return new Dimension(
                        (int)(getWidth() * scaleFactor), 
                        (int)(getHeight() * scaleFactor));
    }

    public Point getVirtualPoint(int x, int y) {
        return new Point(
                        (int)(x * scaleFactor),
                        (int)(y * scaleFactor));
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        Dimension vitualSize = getVirtualSize();
        int xOffset = (getWidth() - vitualSize.width) / 2;
        int yOffset = (getHeight() - vitualSize.height) / 2;

        Graphics2D g2D = (Graphics2D) g.create();
        g2D.setColor(Color.gray);
        g.fillRect(0, 0, image.getWidth(), image.getHeight());

        g2D.setColor(Color.GREEN);
        g2D.drawRect(xOffset, yOffset, vitualSize.width, vitualSize.height);
        g2D.setColor(Color.RED);
        g2D.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight());
        g2D.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);

        Point virtualPoint = getVirtualPoint(imageX, imageY);
        System.out.println(virtualPoint);
        g2D.drawImage(getScaledInstance(), virtualPoint.x + xOffset, virtualPoint.y + yOffset, this);
        g2D.dispose();
    }

    class MouseMotionHandler extends MouseMotionAdapter implements
                    MouseListener, MouseWheelListener {

        public void mousePressed(MouseEvent e) {
            lastMouseX = e.getX();
            lastMouseY = e.getY();
        }

        public void mouseDragged(MouseEvent e) {
            int xDiff = e.getX() - lastMouseX;
            int yDiff = e.getY() - lastMouseY;
            imageX = imageX + xDiff;
            imageY = imageY + yDiff;
            lastMouseX = e.getX();
            lastMouseY = e.getY();
            repaint();
        }

        public void mouseWheelMoved(MouseWheelEvent e) {
            scaled = null;
            int notches = e.getWheelRotation();

            scaleFactor = scaleFactor + notches / 10.0;
            if (scaleFactor < 0.5) {
                scaleFactor = 0.5;
            } else if (scaleFactor > 3.0) {
                scaleFactor = 3.0;
            }

            repaint();
        }

        public void mouseReleased(MouseEvent e) {
        }

        public void mouseEntered(MouseEvent e) {
        }

        public void mouseExited(MouseEvent e) {
        }

        public void mouseClicked(MouseEvent e) {
        }

    }

}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...