I am trying to animate an Image in Xamarin.Forms (version 2.3.3.168). The animation is working, but it does not repeat.
public class WelcomePage : ContentPage
{
Image img;
public WelcomePage()
{
img = new Image
{
Source = "img.png",
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
};
Content = new StackLayout
{
VerticalOptions = LayoutOptions.Center,
Children = {
img
}
};
}
protected override void OnAppearing()
{
base.OnAppearing();
var a = new Animation();
a.Add(0, 0.5, new Animation((v) =>
{
img.Scale = v;
}, 1.0, 1.2, Easing.CubicInOut, () => { System.Diagnostics.Debug.WriteLine("ANIMATION A"); }));
a.Add(0.5, 1, new Animation((v) =>
{
img.Scale = v;
}, 1.2, 1.0, Easing.CubicInOut, () => { System.Diagnostics.Debug.WriteLine("ANIMATION B"); }));
a.Commit(img, "animation", 16, 2000, Easing.Linear, (d, f) => img.Scale = 1.0, () =>
{
System.Diagnostics.Debug.WriteLine("ANIMATION ALL");
return true;
});
}
}
After running the app for a few seconds, the following debug output is printed:
ANIMATION A
ANIMATION B
ANIMATION ALL
ANIMATION ALL
ANIMATION ALL
I am testing this as a UWP.
According to this thread on the Xamarin forums, others seem to be have the same issue. It seems related to a private property being set in each of the sub-animations.
A workaround is to recreate the animation chain each time the chain completes:
public class WelcomePage : ContentPage
{
Image img;
public WelcomePage()
{
img = new Image
{
Source = "circle_plus.png",
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
};
Content = new StackLayout
{
VerticalOptions = LayoutOptions.Center,
Children = {
img
}
};
}
protected override void OnAppearing()
{
base.OnAppearing();
animate();
}
void animate()
{
var a = new Animation();
a.Add(0, 0.5, new Animation((v) =>
{
img.Scale = v;
}, 1.0, 1.2, Easing.CubicInOut, () => { System.Diagnostics.Debug.WriteLine("ANIMATION A"); }));
a.Add(0.5, 1, new Animation((v) =>
{
img.Scale = v;
}, 1.2, 1.0, Easing.CubicInOut, () => { System.Diagnostics.Debug.WriteLine("ANIMATION B"); }));
a.Commit(img, "animation", 16, 2000, null, (d, f) =>
{
img.Scale = 1.0;
System.Diagnostics.Debug.WriteLine("ANIMATION ALL");
animate();
});
}
}
Related
The bounty expires in 6 days. Answers to this question are eligible for a +50 reputation bounty.
Bilal wants to draw more attention to this question.
I want to replicate the existent example of loading Collada kinematics and using them with my own model, to do so I have created a class as follows:
iimport { Object3D, MathUtils } from "three";
import { ColladaLoader } from "three/addons/loaders/ColladaLoader.js";
import yumi_path from "../assets/dae/yumi.dae";
import { Tween, Easing, update } from "#tweenjs/tween.js";
export class YuMiMotion {
constructor(scene, joint_vlaues) {
this.scene = scene;
this.tgt_jnt_vals = joint_vlaues;
this.loader = new ColladaLoader();
this.yumi_model = new Object3D();
this.kinematics;
this.kinematicsTween;
this.tweenParameters = {};
this.loadYuMi();
this.startMotion();
}
startMotion = () => {
if (this.kinematics == undefined) {
console.log("Kinematics Still not loaded!");
this.anim_frame_id1 = requestAnimationFrame(this.startMotion);
}
else {
console.log(this.kinematics);
this.setupTween();
cancelAnimationFrame(this.anim_frame_id1);
this.animate();
}
}
animate = () => {
update();
this.anim_frame_id2 = requestAnimationFrame(this.animate);
}
loadYuMi = async() => {
const onLoad = (result, yumi) => {
const model = result.scene.children[0];
yumi.add(model.clone(true));
yumi.traverse(function (child) {
if (child.isMesh) {
child.material.flatShading = true;
}
});
this.kinematics = result.kinematics;
};
await this.loader.load(yumi_path, (collada) =>
onLoad(collada, this.yumi_model, this.kinematics)
),
undefined,
function (error) {
console.log("An error happened", error);
};
this.yumi_model.position.set(0.0, 0.0, -6.0);
this.yumi_model.rotation.set(-Math.PI / 2, 0.0, -Math.PI / 2);
this.yumi_model.scale.set(20, 20, 20);
this.scene.add(this.yumi_model);
}
setupTween =() =>{
const duration = MathUtils.randInt( 1000, 5000 );
const target = {};
for (const prop in this.tgt_jnt_vals) {
const joint = this.kinematics.joints[ prop ];
const old = this.tweenParameters[ prop ];
const position = old ? old : joint.zeroPosition;
this.tweenParameters[ prop ] = position;
target[prop] = this.tgt_jnt_vals[prop]; //MathUtils.randInt( joint.limits.min, joint.limits.max );
// console.log('target:', prop, this.tgt_jnt_vals[prop]);
}
this.kinematicsTween = new Tween( this.tweenParameters ).to( target, duration ).easing( Easing.Quadratic.Out );
this.kinematicsTween.onUpdate( ( object )=> {
for ( const prop in this.kinematics.joints ) {
if ( prop in this.kinematics.joints) {
if ( ! this.kinematics.joints[ prop ].static ) {
this.kinematics.setJointValue( prop, object[ prop ] );
}
}
}
});
// console.log("Tween Parameters", this.tweenParameters);
// console.log("kinematics tween", this.kinematicsTween);
this.kinematicsTween.start();
setTimeout( this.setupTween, duration );
}
}
And I'm calling this class as follows:
let target_position = {
gripper_l_joint: 0.01,
gripper_l_joint_m: 0.01,
gripper_r_joint: 0.01,
gripper_r_joint_m: 0.01,
yumi_joint_1_l: 10.0,
yumi_joint_1_r: 10.0,
yumi_joint_2_l: 20.0,
yumi_joint_2_r: 20.0,
yumi_joint_3_l: 30.0,
yumi_joint_3_r: 30.0,
yumi_joint_4_l: 40.0,
yumi_joint_4_r: 40.0,
yumi_joint_5_l: 50.0,
yumi_joint_5_r: 50.0,
yumi_joint_6_l: 60.0,
yumi_joint_6_r: 60.0,
yumi_joint_7_l: 70.0,
yumi_joint_7_r: 70.0,
};
new YuMiMotion(this.scene, target_position);
I have created a repo to reproduce my example here.
Can you please tell me how can I Move my model properly to the desired joints position using Three.js and Tween.js? thanks in advance.
I have this code:
public ScrollHeadingView2()
{
outerGrid = new Grid() { BackgroundColor = Color.Red, RowSpacing = 0, ColumnSpacing = 0 };
outerGrid.AddChild(new BoxView()
{
HeightRequest = 100,
WidthRequest = 100,
BackgroundColor = Color.Pink
}, 0, 0);
var tap1 = new TapGestureRecognizer()
{
NumberOfTapsRequired = 1
}.Bind(TapGestureRecognizer.CommandProperty, nameof(BackArrowTapped));
grid1.GestureRecognizers.Add(tap1);
}
private async Task BackArrowTapped()
{
await Shell.Current.Navigation.PopAsync(false);
}
The pink area of the grid displays but when I place a break on the await then it doesn't go there when I tap that area.
Does anyone have any idea what might be wrong?
BackArrowTapped is not a command. If you want to use an event handler, just set the gesture's Tapped property
tap1.Tapped += async (s,o) => { await BackArrowTapped(); };
I am working with chartjs, I am trying to animate chart from right to left or left to right on load.
var canvas = document.getElementById('chart_canvas');
var ctx = canvas.getContext('2d');
// Generate random data to plot
var DATA_POINT_NUM = 10;
var data = {
labels: [],
datasets: [
{
data: [],
},
]
}
for (var i=0; i<DATA_POINT_NUM; i++) {
data.datasets[0].data.push(Math.random()*10);
data.labels.push(String.fromCharCode(65+i));
}
var oldDraw = Chart.controllers.line.prototype.draw;
Chart.controllers.line.prototype.draw = function(animationFraction) {
var animationConfig = this.chart.options.animation;
if (animationConfig.xAxis === true) {
var ctx = this.chart.chart.ctx;
var hShift = (1-animationFraction)*ctx.canvas.width;
ctx.save();
ctx.setTransform(1, 0, 0, 1, hShift,0);
if (animationConfig.yAxis === true) {
oldDraw.call(this, animationFraction);
} else {
oldDraw.call(this, 1);
}
ctx.restore();
} else if (animationConfig.yAxis === true) {
oldDraw.call(this, animationFraction);
} else {
oldDraw.call(this, 1);
}
}
var lineChart = new Chart(ctx, {
type: 'line',
data: data,
options: {
animation: {
duration: 5000,
xAxis: true,
yAxis: true,
}
}
});
Example 1
The above code works fine on windows, but I'm facing issue on mac devices.While animating from left to right the data displays incorrectly means that the data moves to upward from x axis.How to fix this issue?
I am attaching screenshot.
Screenshot
Please change setTransform to transform.
Try the following code
var canvas = document.getElementById('chart_canvas');
var ctx = canvas.getContext('2d');
// Generate random data to plot
var DATA_POINT_NUM = 10;
var data = {
labels: [],
datasets: [
{
data: [],
},
]
}
for (var i=0; i<DATA_POINT_NUM; i++) {
data.datasets[0].data.push(Math.random()*10);
data.labels.push(String.fromCharCode(65+i));
}
var oldDraw = Chart.controllers.line.prototype.draw;
Chart.controllers.line.prototype.draw = function(animationFraction) {
var animationConfig = this.chart.options.animation;
if (animationConfig.xAxis === true) {
var ctx = this.chart.chart.ctx;
var hShift = (1-animationFraction)*ctx.canvas.width;
ctx.save();
ctx.transform(1, 0, 0, 1, hShift,0);
if (animationConfig.yAxis === true) {
oldDraw.call(this, animationFraction);
} else {
oldDraw.call(this, 1);
}
ctx.restore();
} else if (animationConfig.yAxis === true) {
oldDraw.call(this, animationFraction);
} else {
oldDraw.call(this, 1);
}
}
var lineChart = new Chart(ctx, {
type: 'line',
data: data,
options: {
animation: {
duration: 5000,
xAxis: true,
yAxis: true,
}
}
});
I'm trying to access some objects that I create with cs (carousel pages with labels, listview and buttons) but I have not had success.
Here reference this by saying that you can work with classId or styleId, but it does not make it very clear how to get the same:
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/getting_started_with_xaml/
Here you can see the carousel pages
This is my cs code:
private readonly List<AreaModel> _are = new List<AreaModel>();
public AreasModal()
{
Lista();
}
protected override void OnCurrentPageChanged()
{
base.OnCurrentPageChanged();
var index = Children.IndexOf(CurrentPage);
}
private async void Lista()
{
var listareas = await App.NaturalezaManager.GetAreas();
foreach (var Area in listareas) {
var areas = new AreaModel
{
IdArea = Area.IdArea,
NombreArea = Area.NombreArea
};
_are.Add(areas);
Button boton = new Button
{
Text = "Listo",
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand
};
Children.Add(new ContentPage
{
Content = new StackLayout
{
Children =
{
new StackLayout
{
Orientation = StackOrientation.Horizontal,
Children =
{
new StackLayout
{
Orientation = StackOrientation.Vertical,
Children =
{
new Label
{
Text = Area.NombreArea,
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand
}
}
},
new Switch
{
HorizontalOptions = LayoutOptions.EndAndExpand
}
}
},
new ListView
{
ItemsSource = listareas,
ItemTemplate = new DataTemplate(() =>
{
Label nameLabel = new Label
{
HorizontalOptions = LayoutOptions.FillAndExpand
};
nameLabel.SetBinding(Label.TextProperty,"NombreArea");
Switch switcher = new Switch
{
HorizontalOptions = LayoutOptions.EndAndExpand
};
return new ViewCell
{
View = new StackLayout
{
Orientation = StackOrientation.Horizontal,
Children =
{
new StackLayout
{
Orientation = StackOrientation.Vertical,
Children =
{
nameLabel
}
},
switcher
}
}
};
})
},
boton
}
}
});
}
}
}
I have to access these objects from the "OnCurrentPageChanged" method but I have no idea how to do it, since if it were xaml code could be obtained with the property x:Name, in advance thanks for the help.
I have a Xamarin app with a Grid, where there is a Label and an Entry.
When I change the Entry’s text, I want the Label to be visible, and if it is empty, it shall not show the Label.
I have created a demo-app to verify, that it does not have anything to do with my code. But the following code also fails
MainPage = new ContentPage
{
Content = new Grid
{
ColumnDefinitions = new ColumnDefinitionCollection
{
new ColumnDefinition { Width = new GridLength(1,GridUnitType.Auto) },
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }
}
}
};
Label label;
Entry entry;
Grid mainGrid = ((Grid)((ContentPage)MainPage).Content);
mainGrid.Add(btnDelete = new MR.Gestures.StackLayout
{
Children = {
new ExtendedLabel { Style = Styles.LargeLabelIcon, Text = "\ue634", TextColor = Color.Black }
},
WidthRequest = 50
}, 0, 0);
mainGrid.Children.Add(label = new Label { Text = "TEST", IsVisible = false }, 0, 0);
mainGrid.Children.Add(entry = new Entry { }, 1, 0);
entry.TextChanged += (sender, arg) => label.IsVisible = entry.Text.Length > 0;
Does anybody have an idea of how to fix it?
I am not sure if this is what your are trying to achieve:
MainPage = new ContentPage
{
Content = new Grid
{
ColumnDefinitions = new ColumnDefinitionCollection
{
new ColumnDefinition { Width = new GridLength(1,GridUnitType.Auto) },
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }
}
}
};
Label label;
Entry entry;
Grid mainGrid = ((Grid)((ContentPage)MainPage).Content);
mainGrid.Children.Add(label = new Label {
Text = "TEST",
IsVisible = false,
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.Start,
}, 0, 0);
mainGrid.Children.Add(entry = new Entry {
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
BackgroundColor = Color.Black,
TextColor = Color.White
}, 1, 0);
entry.TextChanged += (sender, arg) => label.IsVisible = entry.Text.Length > 0;
I have added vertical and horizontal options to the controllers.
I just had a very similar issue. After the text on my child component was changed, it would be cut-off.
The problem turned out to be that you can't mix a ColumnDefinition of * on the grid with a HorizontalOptions on the child. Having both seems to break something. Replacing it with HorizontalTextAlignment fixed it for me.
Maybe this will help someone else someday.