使用鼠標滾輪縮放時,Java鼠標事件無法正確轉換坐標

我需要一些幫助,弄清楚如何在縮放過程中從鼠標事件轉換坐標。。。當縮放因子為1.0時,它可以工作,但當它改變時,不確定算法。。。

當我注釋掉縮放代碼時,我可以在屏幕周圍拖動矩形,但一旦縮放,鼠標坐標就會在應用縮放后出錯

我就是搞不懂坐標轉換碼

package ca.stackoverflow.main;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

public class Demo extends JFrame {

    private static final long serialVersionUID = 1L;

    public Demo() {
        setPreferredSize(new Dimension(640, 480));
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        JScrollPane scroll = new JScrollPane(new Panel());
        add(scroll, BorderLayout.CENTER);

        pack();
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                try {
                    Demo demo = new Demo();
                    demo.setVisible(true);
                }
                catch(Throwable e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }
}

class Panel extends JPanel implements MouseWheelListener, MouseListener, MouseMotionListener {

    private static final long serialVersionUID = 1L;
    private static final double MIN_ZOOM_FACTOR = 0.1;
    private static final double MAX_ZOOM_FACTOR = 5.0;

    private Color _fillColor;
    private Point _startPoint;
    private double xOffset;
    private double yOffset;

    private int xdragOffset;
    private int ydragOffset;    
    
    private double zoomFactor = 1;
    private double prevZoomFactor = 1;
    private boolean zoomer;
        
    private Rectangle _rectangle;

    public Panel() {
        setPreferredSize(new Dimension(1000, 1000));
        addMouseWheelListener(this);
        addMouseMotionListener(this);
        addMouseListener(this);
        _fillColor = Color.WHITE;
        _rectangle = new Rectangle(50, 50, 100, 200);
    }

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

        Graphics2D g2 = (Graphics2D) g;
        AffineTransform at = new AffineTransform();

        if (zoomer) {

            double xRel = MouseInfo.getPointerInfo().getLocation().getX() - getLocationOnScreen().getX();
            double yRel = MouseInfo.getPointerInfo().getLocation().getY() - getLocationOnScreen().getY();
            double zoomDiv = zoomFactor / prevZoomFactor;
            xOffset = (zoomDiv) * (xOffset) + (1 - zoomDiv) * xRel;
            yOffset = (zoomDiv) * (yOffset) + (1 - zoomDiv) * yRel;
            prevZoomFactor = zoomFactor;
            zoomer = false;
        }
        
        at.translate(xOffset, yOffset);
        at.scale(zoomFactor, zoomFactor);
        g2.transform(at);
        
        
        g.setColor(_fillColor);
        g.fillRect(_rectangle.x, _rectangle.y, _rectangle.width, _rectangle.height);
        g.setColor(Color.BLACK);
        g.drawRect(_rectangle.x, _rectangle.y, _rectangle.width, _rectangle.height);      
    }

    @Override public void mouseMoved(MouseEvent e) {
        _fillColor = Color.WHITE;
        if(_rectangle.contains(e.getPoint())) {
            _fillColor = Color.GREEN;
        }
        repaint();
    }

    @Override public void mousePressed(MouseEvent e) {
        _startPoint = null;
        _fillColor = Color.WHITE;

        if(_rectangle.contains(e.getPoint())) {
            _startPoint = e.getPoint();
            _fillColor = Color.GREEN;
            xdragOffset = _startPoint.x - _rectangle.x;
            ydragOffset = _startPoint.y - _rectangle.y;
        }
        repaint();
    }
    
    @Override public void mouseDragged(MouseEvent e) {
        if(_startPoint != null) {
            int diffX = e.getX() - _startPoint.x;
            int diffY = e.getY() - _startPoint.y;
            
            _rectangle.x = _startPoint.x + diffX - xdragOffset;  
            _rectangle.y = _startPoint.y + diffY - ydragOffset;  
        }
        else {
            _fillColor = Color.WHITE;
            if(_rectangle.contains(e.getPoint())) {
                _fillColor = Color.GREEN;
            }
        }
        repaint();
    }
    
    @Override public void mouseWheelMoved(MouseWheelEvent e) {
        zoomer = true;
        if (e.getWheelRotation() < 0) {
            zoomFactor = Math.max(zoomFactor / 1.1, MIN_ZOOM_FACTOR);
        }
        if (e.getWheelRotation() > 0) {
            zoomFactor = Math.min(zoomFactor * 1.1, MAX_ZOOM_FACTOR);
        }
        repaint();
    }

    @Override public void mouseClicked(MouseEvent e) {}
    @Override public void mouseReleased(MouseEvent e) {} 
    @Override public void mouseEntered(MouseEvent e) {}
    @Override public void mouseExited(MouseEvent e) {}
} 
? 最佳回答:

好吧,這比我最初想的要復雜一些。

“基本”概念是,你需要應用你用來繪制組件的相同AffineTransformation_rectangle

所以,我首先創建了一個實例屬性來跟蹤當前的轉換,因為這將得到一點re-used

private AffineTransform transformation = new AffineTransform();

然后我添加了一個getter

protected AffineTransform getTransformation() {
    return transformation;
}

然后我“優化”了你的paintComponent,真的。。。

double xRel = MouseInfo.getPointerInfo().getLocation().getX() - getLocationOnScreen().getX();
double yRel = MouseInfo.getPointerInfo().getLocation().getY() - getLocationOnScreen().getY();

一般來說,這是一個非常糟糕的主意,但尤其是在油漆通道內

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

    Graphics2D g2 = (Graphics2D) g.create();

    g2.setTransform(getTransformation());

    g2.setColor(_fillColor);
    g2.fillRect(_rectangle.x, _rectangle.y, _rectangle.width, _rectangle.height);
    g2.setColor(Color.BLACK);
    g2.drawRect(_rectangle.x, _rectangle.y, _rectangle.width, _rectangle.height);

    g2.dispose();
}

然后,我修改了mouseWheelMoved以在每次更改時計算新的AffineTransformation

@Override
public void mouseWheelMoved(MouseWheelEvent e) {

    Point2D zoomAnchor = e.getPoint();

    prevZoomFactor = zoomFactor;
    if (e.getWheelRotation() < 0) {
        zoomFactor = Math.max(zoomFactor / 1.1, MIN_ZOOM_FACTOR);
    }
    if (e.getWheelRotation() > 0) {
        zoomFactor = Math.min(zoomFactor * 1.1, MAX_ZOOM_FACTOR);
    }

    transformation = new AffineTransform();
    double zoomDiv = zoomFactor / prevZoomFactor;
    xOffset = (zoomDiv) * (xOffset) + (1 - zoomDiv) * zoomAnchor.getX();
    yOffset = (zoomDiv) * (yOffset) + (1 - zoomDiv) * zoomAnchor.getX();

    transformation.translate(xOffset, yOffset);
    transformation.scale(zoomFactor, zoomFactor);

    repaint();
}

現在,每次調用mouseMoved,都需要將AffineTransformation應用于_rectangle,并檢查它是否存在任何沖突。。。

@Override
public void mouseMoved(MouseEvent e) {
    AffineTransform at = getTransformation();
    PathIterator pathIterator = _rectangle.getPathIterator(at);
    GeneralPath shape = new GeneralPath();
    shape.append(pathIterator, true);
    
    _fillColor = Color.WHITE;
    if (shape.contains(e.getPoint())) {
        _fillColor = Color.GREEN;
    }
    repaint();
}

現在,我很想在每次調用mouseWheelMoved時創建一個“影子”Shape,因為其他鼠標事件都需要它

Runnable example...

我沒有更新你的mousePressedmouseDragged方法,我讓你自己去做。我還留下了一些調試代碼,可以在每個繪制過程中繪制“變換的形狀”。這是在轉換Graphics上下文之前繪制的,所以它應該給您一些指導

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

public class Main extends JFrame {

    private static final long serialVersionUID = 1L;

    public Main() {
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        JScrollPane scroll = new JScrollPane(new TestPane());
        add(scroll, BorderLayout.CENTER);

        pack();
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                try {
                    Main demo = new Main();
                    demo.setVisible(true);
                } catch (Throwable e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

    class TestPane extends JPanel implements MouseWheelListener, MouseListener, MouseMotionListener {

        private static final long serialVersionUID = 1L;
        private static final double MIN_ZOOM_FACTOR = 0.1;
        private static final double MAX_ZOOM_FACTOR = 5.0;

        private Color _fillColor;
        private Point _startPoint;
        private double xOffset;
        private double yOffset;

        private int xdragOffset;
        private int ydragOffset;

        private double zoomFactor = 1;
        private double prevZoomFactor = 1;
        private boolean zoomer;
        private Point zoomAnchor;

        private Rectangle _rectangle;

        private AffineTransform transformation = new AffineTransform();

        public TestPane() {
            addMouseWheelListener(this);
            addMouseMotionListener(this);
            addMouseListener(this);
            _fillColor = Color.WHITE;
            _rectangle = new Rectangle(50, 50, 100, 200);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(640, 480);
        }

        // This would be better pre-calculated when the 
        // zoom actually changes
        protected AffineTransform getTransformation() {
            return transformation;
        }

        private Shape testPath;

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

            Graphics2D g2 = (Graphics2D) g.create();

            if (testPath != null) {
                g2.setColor(Color.RED);
                g2.draw(testPath);
            }

            g2.setTransform(getTransformation());

            g2.setColor(_fillColor);
            g2.fillRect(_rectangle.x, _rectangle.y, _rectangle.width, _rectangle.height);
            g2.setColor(Color.BLACK);
            g2.drawRect(_rectangle.x, _rectangle.y, _rectangle.width, _rectangle.height);

            g2.dispose();
        }

        // This is purly for test purposes and you could simply
        // create based on needs at the time
        // Alternativly, you could apply a simular logic to it as the
        // AffineTransformation and each time the zoom is changed,
        // you could create a new, reusable, instance
        protected Shape createTransformedShape() {
            AffineTransform at = getTransformation();
            PathIterator pathIterator = _rectangle.getPathIterator(at);
            GeneralPath path = new GeneralPath();
            path.append(pathIterator, true);

            testPath = path;
            return path;
        }

        @Override
        public void mouseMoved(MouseEvent e) {
            Shape shape = createTransformedShape();

            _fillColor = Color.WHITE;
            if (shape.contains(e.getPoint())) {
                _fillColor = Color.GREEN;
            }
            repaint();
        }

        @Override
        public void mousePressed(MouseEvent e) {
//            _startPoint = null;
//            _fillColor = Color.WHITE;
//
//            AffineTransform at = getTransformation();
//            Point2D zoomPoint = zoomedPointFrom(e.getPoint());
//            PathIterator pathIterator = _rectangle.getPathIterator(at);
//            GeneralPath path = new GeneralPath();
//            path.append(pathIterator, true);
//
//            if (path.contains(zoomPoint)) {
//                _startPoint = e.getPoint();
//                _fillColor = Color.GREEN;
//                xdragOffset = _startPoint.x - _rectangle.x;
//                ydragOffset = _startPoint.y - _rectangle.y;
//            }
//            repaint();
        }

        @Override
        public void mouseDragged(MouseEvent e) {
//            if (_startPoint != null) {
//                int diffX = e.getX() - _startPoint.x;
//                int diffY = e.getY() - _startPoint.y;
//
//                _rectangle.x = _startPoint.x + diffX - xdragOffset;
//                _rectangle.y = _startPoint.y + diffY - ydragOffset;
//            } else {
//                _fillColor = Color.WHITE;
//                if (_rectangle.contains(e.getPoint())) {
//                    _fillColor = Color.GREEN;
//                }
//            }
//            repaint();
        }

        @Override
        public void mouseWheelMoved(MouseWheelEvent e) {

            Point2D zoomAnchor = e.getPoint();

            prevZoomFactor = zoomFactor;
            if (e.getWheelRotation() < 0) {
                zoomFactor = Math.max(zoomFactor / 1.1, MIN_ZOOM_FACTOR);
            }
            if (e.getWheelRotation() > 0) {
                zoomFactor = Math.min(zoomFactor * 1.1, MAX_ZOOM_FACTOR);
            }

            transformation = new AffineTransform();
            double zoomDiv = zoomFactor / prevZoomFactor;
            xOffset = (zoomDiv) * (xOffset) + (1 - zoomDiv) * zoomAnchor.getX();
            yOffset = (zoomDiv) * (yOffset) + (1 - zoomDiv) * zoomAnchor.getX();

            transformation.translate(xOffset, yOffset);
            transformation.scale(zoomFactor, zoomFactor);

            createTransformedShape();
            repaint();
        }

        @Override
        public void mouseClicked(MouseEvent e) {
        }

        @Override
        public void mouseReleased(MouseEvent e) {
        }

        @Override
        public void mouseEntered(MouseEvent e) {
        }

        @Override
        public void mouseExited(MouseEvent e) {
        }
    }
}
主站蜘蛛池模板: 久久精品一区二区三区AV| 亚洲AV无码一区二区三区人| 色欲AV蜜臀一区二区三区| 亚洲一区二区三区高清在线观看| 精品一区二区三区高清免费观看| 免费看AV毛片一区二区三区| 香蕉久久av一区二区三区| 亚洲国产精品一区二区三区久久 | 无码播放一区二区三区| 亚洲无线码一区二区三区| 一区二区三区在线播放视频| 亚洲av成人一区二区三区观看在线| 国产激情з∠视频一区二区| 亚洲一区二区三区香蕉| 久久无码一区二区三区少妇| 亚洲第一区精品观看| 无码人妻精品一区二区蜜桃百度| 欧美日韩精品一区二区在线视频 | 日韩精品免费一区二区三区| 国产成人精品一区二区A片带套| 亚洲男女一区二区三区| 亚洲综合色一区二区三区小说| 亚洲爆乳精品无码一区二区三区 | 精品无码一区二区三区水蜜桃 | 精品无人区一区二区三区| 日韩一区二区久久久久久| 久久99热狠狠色精品一区| 午夜福利一区二区三区高清视频| 无码人妻一区二区三区在线 | 91精品国产一区| 国产一区二区三区乱码网站| 在线观看亚洲一区二区| 在线播放偷拍一区精品| 国产一区二区三区小说| 免费一区二区三区四区五区| 国产精品一区在线观看你懂的| 国产亚洲日韩一区二区三区 | 国产伦精品一区二区三区精品 | 亚洲国产激情一区二区三区 | 精品国产区一区二区三区在线观看| 免费高清在线影片一区|