Image zoom in Xamarin forms - ios

I want to implement Xamarin forms image zoom property. I did search the internet for quite some time but I didn't find any relevant documentation, can anyone provide me some information on how to implement it in Xamarin Forms?

Currently there is no zoom property for image. You have to use custom renderer to acheive that.
Try using ScaleTo, Itz similar to zoom but not exactly zoom functionality.. Hope it helps...
var img = new Image {
Source = "foo.png",
Scale = .9
};
var stepper = new Stepper {
Minimum = .1,
Maximum = 1,
Increment = .1,
Value = .9,
};
stepper.ValueChanged += async (object sender, ValueChangedEventArgs e) => {
img.ScaleTo(e.NewValue);
};

I had the same need to implement zoom, click, ... on images on Xamarin Forms.
On the forum i find a plugin that define custom renderers for the 3 platforms that can allow you to get coordinates of click on image and then to implement the zoom you want to do.
Here the website of the plugin : http://www.mrgestures.com/
It costs 10euros for every application you develop. Depending on what you need to implement and how many times you will need it it's not expensive at all.

The issue comes with scale property that its scaled to the center of x and y ,
I've implemented this extension no need for extra renderer.
public static Task<bool> ScaleTo(this VisualElement view, double scale, double anchorX, double anchorY, uint length = 250, Easing easing = null)
{
var tcs = new TaskCompletionSource<bool>();
if (easing == null)
{
easing = Easing.Linear;
}
new Animation(
(v) =>
{
view.Scale = scale;
view.AnchorX = anchorX;
view.AnchorY = anchorY;
}, 0, length
).Commit(view, nameof(view), 16, length, easing, finished: (f, a) => tcs.SetResult(a));
return tcs.Task;
}

You can implement pinch to zoom by using a pinch gesture recognizer.
Here is the documentation on pinch to zoom
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/gestures/pinch
At the same time if you want to add pan, you can combine the above code with
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/gestures/pan
Then you would have a pretty good full screen image viewer in Xamarin.Forms

You can use this library, just install on your .netstandard project and that's it!
You can then use Rg.Plugin.Popup and add the image dynamically by providing the link
xmlns:pinch="clr-namespace:Xamarin.Forms.PinchZoomImage;assembly=Xamarin.Forms.PinchZoomImage"
<pinch:PinchZoom.Content>
<Image Source="xxamarin.jpg" />
</pinch:PinchZoom.Content>

Related

OpenLayers - lock rotation of box or rectangle geometry while modifying

Openlayers provides useful functions for drawing boxes and rectangles and also has ol.geom.Geometry.prototype.rotate(angle, anchor) for rotating a geometry around a certain anchor. Is it possible to lock the rotation of a box/rectangle while modifying it?
Using the OpenLayers example located here to draw a box with a certain rotation to illustrate the point:
I would like the box/rectangle to maintain its rotation while still being able to drag the sides longer and shorter. Is there a simple way to achieve this?
Answering with the solution I came up with.
First of all, add the feature(s) to a ModifyInteraction so you are able to modify by dragging the corners of the feature.
this.modifyInteraction = new Modify({
deleteCondition: eventsCondition.never,
features: this.drawInteraction.features,
insertVertexCondition: eventsCondition.never,
});
this.map.addInteraction(this.modifyInteraction);
Also, add event handlers upon the events "modifystart" and "modifyend".
this.modifyInteraction.on("modifystart", this.modifyStartFunction);
this.modifyInteraction.on("modifyend", this.modifyEndFunction);
The functions for "modifystart" and "modifyend" look like this.
private modifyStartFunction(event) {
const features = event.features;
const feature = features.getArray()[0];
this.featureAtModifyStart = feature.clone();
this.draggedCornerAtModifyStart = "";
feature.on("change", this.changeFeatureFunction);
}
private modifyEndFunction(event) {
const features = event.features;
const feature = features.getArray()[0];
feature.un("change", this.changeFeatureFunction);
// removing and adding feature to force reindexing
// of feature's snappable edges in OpenLayers
this.drawInteraction.features.clear();
this.drawInteraction.features.push(feature);
this.dispatchRettighetModifyEvent(feature);
}
The changeFeatureFunction is below. This function is called for every single change which is done to the geometry as long as the user is still modifying/dragging one of the corners. Inside this function, I made another function to adjust the modified rectangle into a rectangle again. This "Rectanglify"-function moves the corners which are adjacent to the corner which was just moved by the user.
private changeFeatureFunction(event) {
let feature = event.target;
let geometry = feature.getGeometry();
// Removing change event temporarily to avoid infinite recursion
feature.un("change", this.changeFeatureFunction);
this.rectanglifyModifiedGeometry(geometry);
// Reenabling change event
feature.on("change", this.changeFeatureFunction);
}
Without going into too much detail, the rectanglify-function needs to
find rotation of geometry in radians
inversely rotate with radians * -1 (e.g. geometry.rotate(radians * (-1), anchor) )
update neighboring corners of the dragged corner (easier to do when we have a rectangle which is parallel to the x and y axes)
rotate back with the rotation we found in 1
--
In order to get the rotation of the rectangle, we can do this:
export function getRadiansFromRectangle(feature: Feature): number {
const coords = getCoordinates(feature);
const point1 = coords[0];
const point2 = coords[1];
const deltaY = (point2[1] as number) - (point1[1] as number);
const deltaX = (point2[0] as number) - (point1[0] as number);
return Math.atan2(deltaY, deltaX);
}

OpenLayers 3 show/hide layers

I am creating an application which utilises a map created and managed by the OpenLayers 3 library. I want to be able to switch which layer is visible using the zoom level (i.e. zoom out to get an overview of countries, zoom in to get an overview of cities). There are three categories of layers (3 different zoom levels), and within each category there are 3 colours which the pins I am using could be (which are all separate layers as well) so in total there are 9 layers.
What I want is to develop the ability to filter which layers are displayed, which means showing/hiding the existing layers depending on which zoom level we are at.
There is some code to demonstrate how the map is generated and how one type of layer is generated but if there is more detail required please let me know. I don't believe there will be an issue with this, however.
function setUpMap(vectorLayers, $scope){
var view = new ol.View({
center: ol.proj.fromLonLat([2.808981, 46.609599]),
zoom: 4
});
map = new ol.Map({
target: 'map',
layers: vectorLayers,
overlays: [overlay],
view: view
});
view.on("change:resolution", function(e){
var oldValue = e.oldValue;
var newValue = e.target.get(e.key);
if (newValue > 35000){
if (oldValue < 35000)
//This is where I will show group 1
} else if (newValue > 10000){
if (oldValue < 10000 || oldValue > 35000)
//This is where I will show group 2
} else {
if (oldValue > 10000)
//This is where I will show group 3
}
});
addClickEventsToMapItems($scope);
}
I tried something like this and got no success:
function showLayer(whichLayer){
vectorLayers[1].setVisibility(false);
vectorLayers[2].setVisibility(false);
vectorLayers[3].setVisibility(false);
vectorLayers[whichLayer].setVisibility(true);
}
I am open to suggestions. Please let me know! :)
You can listen to the resolution:change event on your ol.Map instance:
map.getView().on('change:resolution', function (e) {
if (map.getView().getZoom() > 0) {
vector.setVisible(true);
}
if (map.getView().getZoom() > 1) {
vector.setVisible(false);
}
});
Example on Plunker: http://plnkr.co/edit/szSCMh6raZfHi9s6vzQX?p=preview
It's also worth to take a look at the minResolution and maxResolution options of ol.layer which can switch automaticly for you. But it works by using the view's resolution, not the zoomfactor:
http://openlayers.org/en/v3.2.1/examples/min-max-resolution.html
why dont you use minResolution/maxResolution parameters during vector layer initialasation, check the api here
If you dont know the resolution but you know only the scale, use the following function to get the resolution out of the scale
function getResolutionFromScale(scale){
var dpi = 25.4 / 0.28;
var units = map.getView().getProjection().getUnits();
var mpu = ol.proj.METERS_PER_UNIT[units];
var res = scale / (mpu * 39.37 * dpi);
return res;
}

Use of 'drawPolygonGeometry()' on postCompose event with vectorContext

I'm trying to draw a Circle around every kind of geometry (could be every ol.geom type: point,polygon etc.) in an event called on 'postcompose'. The purpose of this is to create an animation when a certain feature is selected.
listenerKeys.push(map.on('postcompose',
goog.bind(this.draw_, this, data)));
this.draw_ = function(data, postComposeRender){
var extent = feature.getGeometry().getExtent();
var flashGeom = new ol.geom.Polygon.fromExtent(extent);
var vectorContext = postComposeRender.vectorContext;
...//ANIMATION CODE TO GET THE RADIUS WITH THE ELAPSED TIME
var imageStyle = this.getStyleSquare_(radius, opacity);
vectorContext.setImageStyle(imageStyle);
vectorContext.drawPolygonGeometry(flashGeom, null);
}
The method
drawPolygonGeometry( {ol.geom.Polygon} , {ol.feature} )
is not working. However, it works when I use the method
drawPointGeometry({ol.geom.Point}, {ol.feature} )
Even if the type of flashGeom is
ol.geom.Polygon that I just built from an extent. I don't want to use this method because extents from polygons could be received and it animates for every point of the polygon...
Finally, after analyzing the way drawPolygonGeometry in OL3 works in the source code, I realized that I need to to apply the style with this method before :
vectorContext.setFillStrokeStyle(imageStyle.getFill(),
imageStyle.getStroke());
DrawPointGeometry and drawPolygonGeometry are not using the same style instance.

How to create a skin for fl.controls.Slider with custom height in actionscript only?

I created a skin called customSliderTrack in the graphical editor of Adobe Flash CS5.5. This Slider is now in the "library" of the FLA file.
I can apply this skin with the following code:
var cls:Class = getDefinitionByName("CustomSliderTrack") as Class;
var tmpTrack:Sprite = new cls();
slider.setStyle("sliderTrackSkin",tmpTrack);
However due to the binary nature of the FLA file and lack of compatibility of different Versions of Adobe Flash I need to implement it all in Actionscript.
I understand that cls is a MovieClip object but I cant get the same results with new MovieClip(). I think this might be related to the dashed Lines in the graphical editor(I modified the default SliderTrack_skin). I havn't found out yet what they mean and how to replace them with Actionscript code.
setStyle automatically sets the track.height and track.width. In case of the track.height the slider.height attribute does not seem to have any effect. To work around this problem simply set the track.height to the best value.
To access the track extend the Slider class and override the configUI Function:
public class CustomSlider extends Slider
{
override protected function configUI():void
{
// Call configUI of Slider
super.configUI();
// The sprite that will contain the track
var t:Sprite = new Sprite();
// Draw the content into the sprite
t.graphics.beginFill(0x000000, 0.1);
t.graphics.drawRect(0, -15, width, 30);
t.graphics.endFill();
// Set the Sprite to be the one used by the Slider
this.setStyle("sliderTrackSkin",t);
// Reset the height to the value that it should be
track.height = 30;
}
}
Depending on the complexity of your track asset, you could accomplish this with the drawing API: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/Graphics.html
A very simple example would be:
var track:Sprite = new Sprite();
track.graphics.lineStyle(2, 0xcccccc);
track.graphics.beginFill(0x000000, 1);
track.graphics.drawRect(0, 0, 400, 20);
track.graphics.endFill();
track.scale9Grid = new Rectangle(2, 2, 396, 16);
slider.setStyle("sliderTrackSkin",track);
This creates a track that is just a black rectangle, 400x20 pixels in size. You can set the scale9grid in code to control how the skin scales. In the example above the rectangle's border wont scale, but the black rectangle inside will. Experimenting with the methods in the drawing API could be all you need.
If you need a more complex asset, I'd recommend loading an image and then passing that in to slider.setStyle.

Drag, rotate and ease in ActionScript

I need to make a MovieClip rotate around it's center point to the left or right depending on mouse drag. I got some basic semblance of what i want going, but it's pretty hackish. Normally i calculate the angle and such but i only really need to use the distance the mouse travels and apply it to the movieclip and for flavor add some nice easing when you let go.
public function Main()
{
var wereld:MainScreen = new MainScreen();
addChild( wereld );
wereld.x = -510;
planet = wereld["planet"];
wereld.addEventListener( MouseEvent.MOUSE_DOWN, startRotatingWorld );
}
private function startRotatingWorld( e:MouseEvent ):void
{
m_mouseStartPos = stage.mouseX;
stage.addEventListener( MouseEvent.MOUSE_UP, stopRotatingWorld );
stage.addEventListener( Event.MOUSE_LEAVE, stopRotatingWorld);
}
private function applyRotationToWorld( e:MouseEvent ):void
{
//Calculate the rotation
var distance:Number = (m_mouseStartPos - stage.mouseX) / 10000;
//apply rotation
planet.rotation += -distance //* 180 / Math.PI;
}
private function stopRotatingWorld():void
{
stage.removeEventListener( MouseEvent.MOUSE_MOVE, applyRotationToWorld );
}
here is source and demo of a potential solution for you:
http://wonderfl.net/c/o8t0
I based the drag rotation detection on keith peter's minimalcomps, specifically the source for his knob component
In the Minimal Comp's Knob source, you can get rotational, horizontal, and vertical movement in the 'onMouseMoved' function.
Finally I used TweenLite to handle the easing back. In my code I Tween the 'this' object, but in practice you should create a public value in the object you are tweening and tween that item, so you can have more that one item tweening easily.
If this is for a public facing project, let me know when you've implemented it; wouldn't mind taking a look on what it is for ^_^

Resources