From this post i came to know that there exist some platform improvements for
implementing pinch and zoom functionality. By using this new method(ManipulationDeltaEventArgs.PinchManipulation) how i can implement pinch to zoom functionality in windows phone.
Apart from this i need to implement scrolling feature too to the image control. In my current implementation, i am using Toolkit(gesture listener) for pinch and zoom functionality along with scroll viewer, now it seem both scrolling and and pinching events are overlapping and hence it produces a bad user experience.
Can anyone help me to solve this issue in my application. I am looking some code samples that help me to achieve the functionality.
I am not expected to get Multi touch behavior(codeplex) as answer. The assemblies using in the project are quite old and i heard that many of them are facing issues with marketplace submission only because of this.
As I said in my previous answer if you're building a WP8 exclusive app you can use the new ManipulationDeltaEventArgs.PinchManipulation for pinch & zoom effects. Here's a basic example of how to use ManipulationDeltaEventArgs.PinchManipulation data to scale, move and rotate an image.
First, we'll create a basic image hovering in the middle of a grid:
<Grid x:Name="ContentPanel">
<Image Source="Assets\Headset.png"
Width="200" Height="150"
ManipulationDelta="Image_ManipulationDelta"
x:Name="img"
>
<Image.RenderTransform>
<CompositeTransform CenterX="100" CenterY="75" />
</Image.RenderTransform>
</Image>
</Grid>
Next, we'll handle the ManipulationDelta event, check if it's a Pinch Manipulation and apply the correct Silverlight transformations on our UIElement.
private void Image_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
if (e.PinchManipulation != null)
{
var transform = (CompositeTransform)img.RenderTransform;
// Scale Manipulation
transform.ScaleX = e.PinchManipulation.CumulativeScale;
transform.ScaleY = e.PinchManipulation.CumulativeScale;
// Translate manipulation
var originalCenter = e.PinchManipulation.Original.Center;
var newCenter = e.PinchManipulation.Current.Center;
transform.TranslateX = newCenter.X - originalCenter.X;
transform.TranslateY = newCenter.Y - originalCenter.Y;
// Rotation manipulation
transform.Rotation = angleBetween2Lines(
e.PinchManipulation.Current,
e.PinchManipulation.Original);
// end
e.Handled = true;
}
}
// copied from http://www.developer.nokia.com/Community/Wiki/Real-time_rotation_of_the_Windows_Phone_8_Map_Control
public static double angleBetween2Lines(PinchContactPoints line1, PinchContactPoints line2)
{
if (line1 != null && line2 != null)
{
double angle1 = Math.Atan2(line1.PrimaryContact.Y - line1.SecondaryContact.Y,
line1.PrimaryContact.X - line1.SecondaryContact.X);
double angle2 = Math.Atan2(line2.PrimaryContact.Y - line2.SecondaryContact.Y,
line2.PrimaryContact.X - line2.SecondaryContact.X);
return (angle1 - angle2) * 180 / Math.PI;
}
else { return 0.0; }
}
Here's what we did:
Scaling: PinchManipulation actually tracks scaling for us, so all we had to do is apply PinchManipulation.CumulativeScale to the scaling factor.
Transform: PinchManipulation tracks the original center and the new center (calculated between the two touch points). By subtracting the new center from the old center we can tell how much the UIElement needs to move and apply that to a translate transform. Note that a better solution here would also account for multiple Manipulation sessions by tracking cumulative original centers which this code doesn't.
Rotation: We figured out the angle between the two touch points and applied it as the rotation transform. More on that in this Nokia wiki article # Real-time rotation of the Windows Phone 8 Map Control
Here's a few print screens showing this code runs just fine:
I found the perfect soultion for smooth pinch to zoom and pan. It is actually a Microsoft Code sample at the following link
http://code.msdn.microsoft.com/wpapps/Image-Recipes-0c0b8fee
I just used it as boiler plate code and it worked wonders.
Cheers
If you're looking to roll your own, you might want to check out this article about pinch zooming in Silverlight: Implementing pinch zoom correctly in Silverlight
Telerik also has an out of the box pan and zoom image control, but it does cost money: Telerik Pan and Zoom Control
Related
How can I convert, for example, view.WidthRequest value to platform pixels? I'm looking for method like Device.ConvertToPixels(10).
I want to use it for SkiaSharp drawing. For example I want to draw circle with stroke = 10 in (xamarin.forms units) which will be converted to pixels on draw.
Multiply MainDisplayInfo.Density by (xamarin.forms units) and you get that pixels.
I made a method.
double XamDIUConvertToPixels(double XamDIU)
{
var desplayinfo = DeviceDisplay.MainDisplayInfo;
var pixcels = desplayinfo.Density * XamDIU;
return pixcels;
}
DeviceDisplay must be done on the UI thread or else an exception will be thrown in iOS.
read this:
https://learn.microsoft.com/en-us/xamarin/essentials/device-display?tabs=ios#platform-differences
You can also do a scale at the top of the draw and then use whatever units you like.
For example, on a 2x display, you can do a canvas.Scale(2) and then draw as if it was a 1x.
In the case of a Xamarin.Forms paint event, there is the event args that has all the info you need:
canvas.Scale(e.Info.Width / view.Width);
Also, you can have a look at this blog post where I show off some things: https://dotnetdevaddict.co.za/2020/01/12/who-cares-about-the-view-anyway/
You could try the code below:
In Android:
var dp = 100;
int pixel = (int)TypedValue.ApplyDimension(ComplexUnitType.Dip, dp, this.Resources.DisplayMetrics);
In Xamarin.Forms, try to use the Dependency Service.
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/dependency-service/introduction
I developed an script to display 2 layers ob base map.
but it is not zooming properlly.
I am using following code
can you please suggest me
<script type="text/javascript">
var map, layer, select, hover, control;
function init(){
map = new OpenLayers.Map('map', {
controls: [
new OpenLayers.Control.PanZoom(),
new OpenLayers.Control.Permalink(),
new OpenLayers.Control.Navigation()
]
});
layer = new OpenLayers.Layer.WMS(
"States WMS/WFS",
"http://localhost:8080/geoserver/topp/wms",
{layers: 'topp:india_road',transparent: true}
);
select = new OpenLayers.Layer.Vector("Selection", {styleMap:
new OpenLayers.Style(OpenLayers.Feature.Vector.style["select"])
});
hover = new OpenLayers.Layer.Vector("Hover");
map.addLayers([layer, hover, select]);
var wmsLayer = new OpenLayers.Layer.Google('Google Layer',{} );
map.addLayer(wmsLayer);
map.setCenter(new OpenLayers.Bounds(143.83482400000003,-43.648056,148.47914100000003,-39.573891).getCenterLonLat(), 5);
}
</script>
What do you mean by not zooming properly?
Are you finding that they are not matching up with the google maps when zooming in as far as you can go, or the zoom work until you try to pan and then the layers don't stay lined up with google maps? If this is the case then you are probably zooming in further than is supported by the Google map service. There is no easy way to fix this and I recommend you alter allowed scales and zoom with the following settings
minScale - float -- the minimum scale value at which the layer should display
maxScale - float -- the maximum scale value at which the layer should display
numZoomLevels - int -- Total number of zoom levels
Also please see this link on more information about google zoom scales.
If you are finding that none of your layers match the google background. I.E. they look like they are zoomed in out or offset then it is likely that your data is in a different projection to the google maps layer. Google maps uses EPSG:900913 as their SRS. The eisiest way to do this is to let geoserver do the re projection for you. Go to your layer setting in geoserver make sure you have the native projection set correctly and set the declared SRS to EPSG:900913.
If that doesn't help then I think we need more information on exactly what the problem is.
I have some images/sprites/buttons (i tried them all :)) scrolling/moving on the stage. The user has to tap them to remove them.
The problem is that the touchevent does not match the position of the image.
The faster the touchable sprites move, the further the distance between the touch and the actual image. i.e.:
a image is moving across the screen with 20px/frame:
Touching the image triggers nothing, touching 20 before it triggers a touch on the image.
when image is not moving, this doesn't happen. It seems that the image is already moved internally, but not yet drawn to the screen, but thats just a wild guess. The example below uses a button, but the same goes for an image. I"ll provide a short example of the code, but i guess its pretty straightforward what i'm trying to do.
private var _image:Button;
protected function init():void {
//create pickup image
_image = new Button(assets.getTexture("button"));
_image.scaleWhenDown = 1;
_image.addEventListener(Event.TRIGGERED, onClick_image);
addChild(_image);
//listen to touch event (not used, but example for touch on image instead of button
//touchable = true;
//addEventListener(TouchEvent.TOUCH, onTouch_image);
}
private function onEnter_frame(e:Event):void {
_image.x -= 20;
}
I've an app which like the image shown below (sorry I used an iPhone like mock-ups)
The app has an image as background (displayed using Canvas which takes the whole visual screen size), then another shape (the red rectangle in this case) will shown above the background which can be dragged and pinch zoomed.
Now the question is:
How can I get the coordinates (origins from top-left corner of the screen, i.e. top-left of the canvas) of the top-left corner of the rectangle?
UPDATE
According to #Will's suggestion, I currently move the rectangle in this case via DragDelta using TranslateTransform inside it, like:
rectTransform.X += e.HorizonalChange;
rectTransform.Y += e.VerticalChange;
The rectangle is defined within code, not in XAML:
rect.Stroke = new SolidColorBrush(Colors.Green);
rect.StrokeThickness = 10;
rect.Fill = new SolidColorBrush(Colors.Transparent);
rect.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
rect.VerticalAlignment = System.Windows.VerticalAlignment.Center;
rect.Width = 200;
rect.Height = 200;
canvas.Children.Add(rect);
and the canvas is defined within XAML.
What I've tried:
Initially I was trying to get the start point when the Drag event begins, but by using the DragStarted(object sender, DragStartedGestureEventArgs e), I am only able to output the coords of the point which was touched, but not the point of top-left corner of this rectangle.
And it's similar to the DragCompleted event which will return me the point the gesture ends.
So is there any chance I can get the origin coords of the red rectangle?
I spent nearly an afternoon on Google as well as MSDN and then finally come to find this on SO:
How to get the position of an element in a StackPanel?
which enlightened me using the similar method. In that case, they managed to get the absolute coordinates of an UI Element. Similarily, in my case, I intend to know the absolute origin(coordinates) of the red rectangle to the background canvas.
I use:
GeneralTransform gt = canvas.TransformToVisual(rect);
Point currentPos = gt.Transform(new Point(0, 0));
Then the currentPos is the current position of rectangle (its an absolute position, relative to the canvas).
And Here's the reference of TransformToVisual in MSDN
There's only one thing to pay attention to: the value of X and Y are negative.
Not 100% sure of the syntax here, but in WPF (which should be analogous) I'd handle it by
private double _startX, startY, _endX, _endY;
private void DragStarted(object sender, DragStartedGestureEventArgs e)
{
var rect = sender as UIElement; // I'm assuming the sender is your Rectangle
this._startX = Canvas.GetLeft(rect);
this._startY = Canvas.GetTop(rect);
}
private void DragCompleted(object sender, DragCompletedGestureOrWhateverLolEventArgs e)
{
var rect = sender as UIElement; // I'm assuming the sender is your Rectangle
this._endX = Canvas.GetLeft(rect);
this._endY = Canvas.GetTop(rect);
}
What you say? Not type safe? Yeah, well, okay. How about give your rectangle an x:Name and then use that in these methods?
<Rectangle x:Name="rect" SkipOtherPropertiesLol="true" />
and
this._startX = Canvas.GetLeft(rect); //etc
Attached properties are a little confusing at first, but they are very powerful and easy to use once you understand their fairy magic.
I've written a home-brew view_port class for a 2D strategy game. The panning (with arrow keys) and zooming (with mouse wheel) work fine, but I'd like the view to also home towards wherever the cursor is placed, as in Google Maps or Supreme Commander
I'll spare you the specifics of how the zoom is implemented and even what language I'm using: this is all irrelevant. What's important is the zoom function, which modifies the rectangle structure (x,y,w,h) that represents the view. So far the code looks like this:
void zoom(float delta, float mouse_x, float mouse_y)
{
zoom += delta;
view.w = window.w/zoom;
view.h = window.h/zoom;
// view.x = ???
// view.y = ???
}
Before somebody suggests it, the following will not work:
view.x = mouse_x - view.w/2;
view.y = mouse_y - view.h/2;
This picture illustrates why, as I attempt to zoom towards the smiley face:
As you can see when the object underneath the mouse is placed in the centre of the screen it stops being under the mouse, so we stop zooming towards it!
If you've got a head for maths (you'll need one) any help on this would be most appreciated!
I managed to figure out the solution, thanks to a lot of head-scratching a lot of little picture: I'll post the algorithm here in case anybody else needs it.
Vect2f mouse_true(mouse_position.x/zoom + view.x, mouse_position.y/zoom + view.y);
Vect2f mouse_relative(window_size.x/mouse_pos.x, window_size.y/mouse_pos.y);
view.x = mouse_true.x - view.w/mouse_relative.x;
view.y = mouse_true.y - view.h/mouse_relative.y;
This ensures that objects placed under the mouse stay under the mouse. You can check out the code over on github, and I also made a showcase demo for youtube.
In my concept there is a camera and a screen.
The camera is the moving part. The screen is the scalable part.
I made an example script including a live demo.
The problem is reduced to only one dimension in order to keep it simple.
https://www.khanacademy.org/cs/cam-positioning/4772921545326592
var a = (mouse.x + camera.x) / zoom;
// now increase the zoom e.g.: like that:
zoom = zoom + 1;
var newPosition = a * zoom - mouse.x;
camera.setX(newPosition);
screen.setWidth(originalWidth * zoom);
For a 2D example you can simply add the same code for the height and y positions.