I'm attempting to sort of "highlight" a tile object within a game I'm making (Mahjong Solitaire). To do this, I'm drawing a Rectangle2D object in the same position as the tile and trying to have it display when the mouse is clicked.
I'm able to get the mouse click event to work and recognize when tiles are selected, but for some reason, the rectangle is not drawn when I'm within the mousePressed function. I can't seem to figure out why...
Here is what I consider the relevant code - I can expand it if necessary!
/* Above this, the positions of tiles are set */
if (content[i][y][x].isVisible()) {
/* Draws the image to screen at the appropriate location */
graphics.drawImage(image, x*TILEW+TILEW/2+i*TILESKEW, (y+1)*TILEH/2-i*TILESKEW,null);
}
/* Represents the area around a tile, so that you can determine
* whether appropriate area pressed within a tile */
final Rectangle2D rect = new Rectangle2D.Double(x*TILEW+TILEW/2+i*TILESKEW,(y+1)*TILEH/2-i*TILESKEW, image.getWidth(null), image.getHeight(null));
/* Set colour of border rectangle */
graphics.setColor(Color.red);
/* Store positions and sizes of tile objects */
final int xPos = x*TILEW+TILEW/2+i*TILESKEW;
final int yPos = (y+1)*TILEH/2-i*TILESKEW;
final int height = image.getHeight(null)+2;
/* This works - outside of the mouse event */
//graphics.drawRoundRect(xPos, yPos, width, height, 7, 7);
/* Mouse event */
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
/* Draw the rectangle to the screen -- Doesn't display! */
graphics.drawRoundRect(xPos, yPos, width, height, 7, 7);
}
The "graphics" Graphic object is passed to the function:
public void paintComponent(final Graphics graphics) { ... }
Any suggestions would be greatly appreciated!
Thank you in advance for your help!
Your program structure sounds off in that you usually shouldn't have the MouseListener directly manipulating the Graphics object that is passed into paintComponent. The reason for this is that the Graphics object obtained this way won't persist. Usually you'll have the MouseAdapter (both MouseListener and MouseMotionListener) alter class fields and then call repaint() on the component. Then paintComponent uses the fields set by the mouse adapter to draw the rectangle.
Edit 1
For example, please see my sample program here: drawing-a-rectangle-over-an-existing-graphics-page
Related
I have a very simple problem. I am learning Java, and was given an assignment to draw a car. I did this all in one class that extends JPanel, and did the drawing within paintComponent().
I realize this is poor object-oriented programming, and decided to try to subclass some of the parts to rectify this situation.
I tried to create a class that draws wheels, but was unsuccessful.
Essentially, I wanted to be able to do this:
Main Class extends JPanel
paintComponent{
Wheel leftWheel = new Wheel(0, 50, 100);
this.add(leftWheel);
}
This should draw a wheel at the point (0, 50) within the JPanel, and have a diameter of 100.
However, i'm unsure how i'm supposed to control the positioning in the JPanel. When I do this, the wheel in drawn at the top center of my window. This is what my wheel class looks like:
public class Wheel extends JComponent {
private int x, y, diameter;
private boolean clockwise;
Wheel(int x, int y, int size, boolean cw)
{
this.setPreferredSize(new Dimension(size, size));
this.x = x;
this.y = y;
diameter = size;
clockwise = cw;
repaint();
}
public void paintComponent(Graphics canvas)
{
super.paintComponent(canvas);
canvas.setColor(Color.gray);
canvas.fillOval(x,y,diameter,diameter);
}
}
The x and y should be where it appears on the parent window, however, this is not the case in the following code (located in the parent class that extends JFrame):
Wheel leftWheel = new Wheel(0,0,WHEEL_DIAMETER,true);
this.add(leftWheel);
The wheel doesn't draw at the top left of my window, it draws in the center of my window at the top. Am I doing something incorrectly? Sorry if I don't follow some Java conventions, I don't have any experience yet. Is this how I should be handling the drawing of the wheel, or is there a more accepted practice for doing this type of drawing?
For example, in my JPanel class, if I add the following code:
Wheel x = new Wheel(50,60,75,true);
this.add(x);
I get a frame sized 75x75 in which a wheel (sized 75x75) is drawn at the point (50,60) within that frame, not within the parent JPanel's coordinate system. The result is a circle that gets clipped and I only see the top left of the circle. That image is displayed at the top center of my JPanel
I understand how to draw the wheel, and move it within itself, but how do I position the wheel on the JPanel??
Your constructor has a small bug,
Wheel(int x, int y, int size, boolean cw) {
this.setPreferredSize(new Dimension(size, size));
diameter = size;
clockwise = cw;
repaint();
}
You forgot to store x and y. I think you wanted,
Wheel(int x, int y, int size, boolean cw) {
this.x = x;
this.y = y;
this.setPreferredSize(new Dimension(size, size));
diameter = size;
clockwise = cw;
repaint();
}
Because your x and y are 0 if you don't set them.
Could you explain how to control it's location within the JPanel, not within itself please?
The default LayoutManager for a JPanel is a FlowLayout so the component will always be positioned based on the rules of the layout manager.
If you want to add components to a random location then you need to use a null layout. But when you use a null layout you are then responsible for setting the size and location of the component. So, in reality the custom painting should always be done at (0, 0) in your custom component.
Instead of adding multiple JPanels to create the vehicle I would simply use one class that extends JPanel and create multiple methods to create things such as wheels etc. to be called from within the overridden paintComponent method. You can pass the new method a reference of your graphics object, create a copy of your graphics object using g.create(), or use getGraphics() from inside the method itself. Inside the method to create a wheel you then are able to calculate it's position by using the panel's dimensions and place it properly.
An alternative would be to define and return shapes in other methods and simply draw them using the graphics object in paintComponent().
I have two JPanels. One panel has a 100x100 rectangle drawn at 0,0. And the other has a 100x100 rectangle drawn at 100, 100. My problem is that when both JPanels are drawn on the JFrame's content pane, one JPanel (the last one drawn) covers the other, hiding its graphics. Below is oversimplified code drawing two rectangles and the things I've tried.
package playground;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Playground{
public Playground(){
JFrame frame = new JFrame("My Frame");
frame.setSize(400, 400);
JPanel backPanel = new JPanel(){;
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
Rectangle2D rect = new Rectangle2D.Double(0, 0, 100, 100);
g2.draw(rect);
}
};
JPanel frontPanel = new JPanel(){
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
Rectangle2D rect = new Rectangle2D.Double(150, 150, 100, 100);
g2.draw(rect);
}
};
frontPanel.setOpaque(true); //Does nothing
frontPanel.setBackground(new Color(0, 0, 0, 0)); //Does nothing
frontPanel.setForeground(new Color(0, 0, 0, 0)); //Erases the rectangle drawn
frame.getContentPane().add(backPanel);
frame.getContentPane().add(frontPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args){
System.out.println("Hello World");
new Playground();
}
}
If anyone cares why I want to do this,
I'm creating the game breakout. I am a novice programmer and I have no knowledge of gaming theory. So I decided the smartest way to avoid a lot of rendering and buffering is to have four JPanels. A static JPanel at the very back with an image drawn on it (fun background image). A JPanel with the paddle drawn on it. A JPanel with bricks drawn on it. And a JPanel with a ball drawn on it. My rationale is that I won't have to redraw the paddle if it is not being moved, the background, and bricks that are not being hit. If a brick lets say is hit, I will update an arrayList of bricks and call repaint on the corresponding JPanel.
I am a novice programmer and I have no knowledge of gaming theory.
Ok, we can work with that.
So I decided the smartest way to avoid a lot of rendering and buffering is to have four JPanels.
You've just unnecessarily complicated your program.
Think of a JPanel as a canvas. You want to draw the entire Breakout game; bricks, paddle, and ball, on one JPanel canvas. Don't worry, you'll be able to redraw the entire canvas fast enough to get 60 frames per second if you want.
The way to do this is to create a Brick class, a Paddle class, and a Ball class. You create a Game Model class that contains one instance of the Paddle class, one instance pf the Ball class, and a List of instances of the Brick class.
The Brick class would have fields to determine its position in the wall, the number of points scored when the ball collides with the brick, the color of the brick, and a draw method that knows how to draw one brick.
The ball class would have fields to determine its x, y position, its direction, its velocity, and a draw method that knows how to draw the ball.
The Paddle class would have fields to determine its x, y position, its direction, its velocity, and a draw method that knows haw to draw the paddle.
The Game Model class would have methods to determine when the ball collides with a brick, determine when the ball collides with a brick, determine when the ball collides with a wall, and a draw method that calls the other model class draw methods to draw a ball, a paddle, and a wall of bricks.
This should be enough for now to get you started going in the right direction.
Edited to answer questions:
How would I implement a draw method in all these classes?
Here's an example Ball class. I haven't tested the moveBall method, so it might need some adjustment
import java.awt.Graphics;
import java.awt.geom.Point2D;
public class Ball {
private Point2D position;
/** velocity in pixels per second */
private double velocity;
/**
* direction in radians
* <ul>
* <li>0 - Heading east (+x)</li>
* <li>PI / 2 - Heading north (-y)</li>
* <li>PI - Heading west (-x)</li>
* <li>PI * 3 / 2 - Heading south (+y)</li>
* </ul>
* */
private double direction;
public Point2D getPosition() {
return position;
}
public void setPosition(Point2D position) {
this.position = position;
}
public double getVelocity() {
return velocity;
}
public void setVelocity(double velocity) {
this.velocity = velocity;
}
public double getDirection() {
return direction;
}
public void setDirection(double direction) {
this.direction = direction;
}
public void moveBall(long milliseconds) {
Point2D oldPosition = position;
// Calculate distance of ball motion
double distance = velocity / (1000.0D * milliseconds);
// Calculate new position
double newX = distance * Math.cos(direction);
double newY = distance * Math.sin(direction);
newX = oldPosition.getX() + newX;
newY = oldPosition.getY() - newY;
// Update position
position.setLocation(newX, newY);
}
public void draw(Graphics g) {
int radius = 3;
int x = (int) Math.round(position.getX());
int y = (int) Math.round(position.getY());
// Draw circle of radius and center point x, y
g.drawOval(x - radius, y - radius, radius + radius, radius + radius);
}
}
The draw method draws the ball wherever it actually is located. That's all the draw method does.
Actually moving the ball is the responsibility of the Game Model class. The method for moving the ball is included in this class because the information necessary to move the ball is stored in the Ball class.
I gave the ball a radius of 3, or a diameter of 6 pixels. You may want to make the ball bigger, and use the fillOval method instead of drawOval.
should I just call repaint() at a 30ms interval
Basically, yes.
In psudeocode, you create a game loop
while (running) {
update game model();
draw game();
wait;
}
First, you update the game model. I gave you a Ball class. You would have similar classes for the paddle and bricks. They all have draw methods.
Your Game model class calls all of these draw methods in the proper order. In Breakout, you would draw the boundaries first, then the bricks, then the paddle, and finally, the ball.
Your JPanel (canvas) calls the one draw method in the Game Model class.
I don't have an example game to show you, but if you read the article Sudoku Solver Swing GUI, you'll see how to put together a Swing GUI and you'll see how model classes implement draw methods.
I suggest that you stop working on Breakout for a while and go through the Oracle Swing Tutorial. Don't skip any sections in your haste to write a program. Go through the entire tutorial so you understand how Swing works before you try and use it.
When using the Graphics2D scale() function with two different parameters (scaling by different ratios in x- and y-direction), everything drawn later on this Graphics2D object is scaled too. This has the strange effect that lines drawn in one direction are thicker than those in another direction. The following program produces this effect, it shows this window:
public class StrokeExample extends JPanel {
public void paintComponent(Graphics context) {
super.paintComponent(context);
Graphics2D g = (Graphics2D)context.create();
g.setStroke(new BasicStroke(0.2f));
int height = getHeight();
int width = getWidth();
g.scale(width/7.0, height/4.0);
g.setColor(Color.BLACK);
g.draw(new Rectangle( 2, 1, 4, 2));
}
public static void main(String[] params) {
EventQueue.invokeLater(new Runnable(){public void run() {
StrokeExample example = new StrokeExample();
JFrame f = new JFrame("StrokeExample");
f.setSize(100, 300);
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(example);
f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
f.setVisible(true);
}});
}
}
I'm using this coordinate transform to avoid having to manually transform my application model coordinates (the (2,1, 2,4) in this example) to screen (or component) pixel coordinates, but I don't want this stroke distortion. In other words, I want to have all lines the same width, independent of current x- and y-scale-factors.
I know what produces this effect (the Stroke object creates a stroked shape of the rectangle to be painted in user coordinates, which then are translated to screen coordinates), but I'm not sure on how to solve this.
Should I create a new Stroke implementation which strokes Shapes differently in X- and Y-direction (thereby undoing the distortion here)? (Or does anyone already knows such an implementation?)
Should I transform my shapes to screen coordinates and stroke there?
Any other (better) ideas?
Turns out my question was not so horrible difficult, and that my two ideas given in the question are actually the same idea. Here is a TransformedStroke class which implements a distorted Stroke by transforming the Shape.
import java.awt.*;
import java.awt.geom.*;
/**
* A implementation of {#link Stroke} which transforms another Stroke
* with an {#link AffineTransform} before stroking with it.
*
* This class is immutable as long as the underlying stroke is
* immutable.
*/
public class TransformedStroke
implements Stroke
{
/**
* To make this serializable without problems.
*/
private static final long serialVersionUID = 1;
/**
* the AffineTransform used to transform the shape before stroking.
*/
private AffineTransform transform;
/**
* The inverse of {#link #transform}, used to transform
* back after stroking.
*/
private AffineTransform inverse;
/**
* Our base stroke.
*/
private Stroke stroke;
/**
* Creates a TransformedStroke based on another Stroke
* and an AffineTransform.
*/
public TransformedStroke(Stroke base, AffineTransform at)
throws NoninvertibleTransformException
{
this.transform = new AffineTransform(at);
this.inverse = transform.createInverse();
this.stroke = base;
}
/**
* Strokes the given Shape with this stroke, creating an outline.
*
* This outline is distorted by our AffineTransform relative to the
* outline which would be given by the base stroke, but only in terms
* of scaling (i.e. thickness of the lines), as translation and rotation
* are undone after the stroking.
*/
public Shape createStrokedShape(Shape s) {
Shape sTrans = transform.createTransformedShape(s);
Shape sTransStroked = stroke.createStrokedShape(sTrans);
Shape sStroked = inverse.createTransformedShape(sTransStroked);
return sStroked;
}
}
My paint-method using it then looks like this:
public void paintComponent(Graphics context) {
super.paintComponent(context);
Graphics2D g = (Graphics2D)context.create();
int height = getHeight();
int width = getWidth();
g.scale(width/4.0, height/7.0);
try {
g.setStroke(new TransformedStroke(new BasicStroke(2f),
g.getTransform()));
}
catch(NoninvertibleTransformException ex) {
// should not occur if width and height > 0
ex.printStackTrace();
}
g.setColor(Color.BLACK);
g.draw(new Rectangle( 1, 2, 2, 4));
}
Then my window looks like this:
I'm quite content with this, but if someone has more ideas, feel free to answer nevertheless.
Attention: This g.getTransform() is returning the complete transformation of g relative to the device space, not only the transformation applied after the .create(). So, if someone did some scaling before giving the Graphics to my component, this would still draw with a 2-device-pixel width stroke, not 2 pixels of the grapics given to my method. If this would be a problem, use it like this:
public void paintComponent(Graphics context) {
super.paintComponent(context);
Graphics2D g = (Graphics2D)context.create();
AffineTransform trans = new AffineTransform();
int height = getHeight();
int width = getWidth();
trans.scale(width/4.0, height/7.0);
g.transform(trans);
try {
g.setStroke(new TransformedStroke(new BasicStroke(2f),
trans));
}
catch(NoninvertibleTransformException ex) {
// should not occur if width and height > 0
ex.printStackTrace();
}
g.setColor(Color.BLACK);
g.draw(new Rectangle( 1, 2, 2, 4));
}
In Swing normally your Graphics given to the paintComponent is only translated (so (0,0) is the upper left corner of your component), not scaled, so there is no difference.
There is a simpler and less 'hacky' solution than the original TransformedStroke answer.
I got the idea when I read how the rendering pipeline works:
(from http://docs.oracle.com/javase/7/docs/technotes/guides/2d/spec/j2d-awt.html)
If the Shape is to be stroked, the Stroke attribute in the Graphics2D context is used to generate a new Shape that encompasses the stroked path.
The coordinates of the Shape’s path are transformed from user space into device space according to the transform attribute in the Graphics2D context.
The Shape’s path is clipped using the clip attribute in the Graphics2D context.
The remaining Shape, if any, is filled using the Paint and Composite attributes in the Graphics2D context.
What you, and I, ideally seek is a way to swap the first two steps.
If you look closely at the second step, TransformedStroke already contains part of the solution.
Shape sTrans = transform.createTransformedShape(s);
solution
In stead of:
g.scale(...), g.transform(...), whatever,
g.draw(new Rectangle( 1, 2, 2, 4));
Or, using TransformedStroke:
g.setStroke(new TransformedStroke(new BasicStroke(2f), g.getTransform());
g.draw(new Rectangle( 1, 2, 2, 4));
I propose you do:
transform =whatever,
g.draw(transform.createTransformedShape(new Rectangle( 1, 2, 2, 4));
Don't transform g anymore. Ever. Transform the shapes instead, using a transform that you make and modify yourself.
discussion
TransformedStroke feels more like a 'hack' than a way the authors of Stroke meant the interface to be used. It also requires an extra class.
This solution keeps a separate Transform around and modifies the Shape instead of transforming the Graphics object. This is however in no way a hack, because I'm not abusing existing functionality but using API functionality exactly how it's meant to be used. I'm just using the more explicit parts of the API instead of the 'shortcut'/'convenience' methods of the API (g.scale() etc.).
Performance-wise, this solution can only be more efficient. Effectively one step is now skipped. In the original solution, TransformedStroke transforms the shape twice and strokes the shape once. This solution transforms the shape explicitly and the *current* stroke strokes the shape once.
Have you just tried to make the int x and int y on the application bigger like int x = 500 int y = 900??? Also my suggestion is that with out rewritten the whole code is to implement where the recs are thicker when the app is closer together more like doubling the rectangle on the top and the bottom but when the app is extended the recs on the top and bottom go back to normal...
I have a field which extends BitmapField (called AnimatedGIFField) and an AnimatorThread (extending Thread) which does the work of looping through the GIF frames, incrementing the current frame and invalidating the field, which calls the paint() method to draw the next frame (resulting in animation). The animation code works fine, but my issue is in the paint() method of the AnimatedGIFField class. I'm calling 'graphics.drawImage()' and I'm having trouble getting proper positions for x and y (the first two args to drawImage()). Positioning the AnimatedGIFField is working and is accomplished by overriding 'getPreferredWidth()' and 'getPreferredHeight()'. The relevant code is here:
public class AnimatedGIFField extends BitmapField {
/**
The current frame in the animation sequence; the AnimatorThread
increments this so paint() knows which frame to draw.
*/
private int currentFrame;
public AnimatedGIFField(GIFEncodedImage image, long style) {
super(image.getBitmap(), style);
this.image = image;
this.preferredWidth = this.image.getWidth();
this.preferredHeight = -(this.image.getHeight() * 4);
}
protected void paint(Graphics graphics) {
// Calling super draws the first background frame.
super.paint(graphics);
// Don't redraw if this is the first frame.
if (this.currentFrame != 0) {
// Draw the animation frame.
/* getFrameLeft() and getFrameTop() both return the top left-most position (0, 0). */
/* graphics.drawImage(this.image.getFrameLeft(this.currentFrame), */
/* this.image.getFrameTop(this.currentFrame), */
/* this.image.getFrameWidth(this.currentFrame), */
/* this.image.getFrameHeight(this.currentFrame), */
/* this.image, this.currentFrame, 0, 0); */
/*
Currently trying some hackish nonsense like this to position the frame, although
it probably won't scale properly across different devices/screen sizes.
*/
int x = (this.getManager().getWidth() / 2) - 45;
int y = (this.getManager().getHeight() / 2) + 83;
graphics.drawImage(x, y,
this.image.getFrameWidth(this.currentFrame),
this.image.getFrameHeight(this.currentFrame),
this.image, this.currentFrame, 0, 0);
}
}
}
What about something like this?
graphics.drawImage(this.image.getFrameLeft(this.currentFrame,
this.image.getFrameTop(this.currentFrame),
this.image.getFrameWidth(this.currentFrame),
this.image.getFrameHeight(this.currentFrame),
this.image, this.currentFrame, 0, 0);
Fixed the problem by stripping out all getPreferredWidth/Height stuff, removed the sublayout() method on my custom field and just overwrote layout() inside of my custom manager to position all fields (just the one) properly. That caused image.getFrameLeft() and image.getFrameTop() return proper values, which is where I had my positioning hacks before.
Thanks for the responses. I was making it way more complicated than it needed to be.
I'm doing these iTunes Stanford classes, and I've been learning beginning Java. Things are going great, but they recently introduced events-and specifically MouseEvents. I've been reading the chapters in the book, and pouring through the example code, and something is just not clicking right for me...it's always that asynchronous stuff that gives me trouble :-D
Earlier, some people mentioned it was important that I mention that the "addMouseListener" is a class in the Graphics import. As far as I can tell, that just adds a blanket mouse listener to the canvas.
I'm still real new to this, so I may not be describing things as well as I should.
This is a piece of code that I have been trying to simplify in order to better understand it. Currently, it will build a red rectangle, and I can click on it and drag it along the x axis. Great!!!
import java.awt.*;
import java.awt.event.*;
import acm.graphics.*;
import acm.program.*;
/** This class displays a mouse-draggable rectangle and oval */
public class DragObject extends GraphicsProgram {
/* Build a rectangle */
public void run() {
GRect rect = new GRect(100, 100, 150, 100);
rect.setFilled(true);
rect.setColor(Color.RED);
add(rect);
addMouseListeners();
}
/** Called on mouse press to record the coordinates of the click */
public void mousePressed(MouseEvent e) {
lastX = e.getX();
lastY = e.getY();
gobj = getElementAt(lastX, lastY);
}
/** Called on mouse drag to reposition the object */
public void mouseDragged(MouseEvent e) {
if((lastX) > 100){
gobj.move(e.getX() - lastX, 0);
lastX = e.getX();
lastY = e.getY();
}
}
/** Called on mouse click to move this object to the front */
public void mouseClicked(MouseEvent e) {
if (gobj != null) gobj.sendToFront();
}
/* Instance variables */
private GObject gobj; /* The object being dragged */
private double lastX; /* The last mouse X position */
private double lastY; /* The last mouse Y position */
}
If I drag the mouse off the canvas, I want the rectangle to stay within the canvas, and not move off it (the same behavior that a horizontal scroll bar would do if you moved beyond the scroll area with the mouse button still clicked). How can I do that?
I've been trying something along these lines, but it's not working right:
if ( ( lastX > (getWidth() - PADDLE_WIDTH) ) || ( lastX < PADDLE_WIDTH ) ) {
gobj.move(0, 0);
} else {
gobj.move(e.getX() - lastX, 0);
}
Your code is moving the rectangle relative to the last position of the mouse. This works fine when you are simply moving things, but for your needs when you want it to stop at the borders, you need to use absolute positioning.
// When the mouse is pressed, calculate the offset between the mouse and the rectangle
public void mousePressed(MouseEvent e) {
lastX = e.getX();
lastY = e.getY();
gobj = getElementAt(lastX, lastY);
}
public void mouseDragged(MouseEvent e) {
double newX;
// Assuming you can get the absolute X position of the object.
newX = gobj.getX() + e.getX() - lastX;
// Limit the range to fall within your canvas. Adjust for your paddle width as necessary.
newX = Math.max( 0, Math.min( newX, getWidth() ) );
// Set the new position of the paddle, assuming you can set the absolute position.
gobj.setX( newX );
lastX = e.getX();
lastY = e.getY();
}
}
This may not be quite what you want because as soon as you go off the edge, the object will stop moving, but then once you move back toward the canvas, your paddle will move immediately instead of waiting for the mouse to reach the same relative position to the paddle at which it started.
You can probably experiment to get it to do what you want.
In order to do this you will need to know the width of the Canvas object, i'm sure there will be a method that will provide this value. You can then check the current x location of the MouseEvent against the width of the canvas, and not increment the x coordinates of the shape object once you are past the width of the canvas. Depending on how much of the shape you want to remain in the canvas, you may need to take into account the width of the shape object as well.
One thing that helps me when dealing w/ animation and moving objects in a gui is drawing out a few scenarios on paper, and noting how the coordinates change.