How to disable animation in sap.m.ProgressIndicator on "percentValue" change? - sapui5

As the title says, how to make the sap.m.ProgressIndicator not animated when changing the percent value of it?
I cannot find a method for it, and extending would probably be the way to go, but maybe somebody has already figured it out and done it?
My Google search was not successful though.

interesting question, below is the sap.m.ProgressIndication.prototype.setPercentValue function, you can see when the percent value changes the bars values is changed via an linear animation
My suggestion, the easiest way to change this behavior is to extend the control to your own control and to redefine the setPercentValue, either remove the animate function on the bar or set time to null so there is no animation
sap.m.ProgressIndicator.prototype.setPercentValue = function(fPercentValue) {
var that = this;
...
if (that.getPercentValue() != fPercentValue) {
// animation without rerendering
this.$().addClass("sapMPIAnimate");
var time = Math.abs(that.getPercentValue() - fPercentValue) * 20;
this.setProperty("percentValue", fPercentValue, true);
var $Bar = this.$("bar");
$Bar.animate({
width : fPercentValue + "%"
}, time, "linear", function() {
that._setText.apply(that);
that.$().removeClass("sapMPIAnimate");
});
}
something like
jQuery.sap.declare("my.ProgressIndicator");
jQuery.sap.require("sap.m.ProgressIndicator");
sap.m.ProgressIndicator.extend("my.ProgressIndicator", {
renderer: {}
});
my.ProgressIndicator.prototype.setPercentValue = function(fPercentValue) {
var that = this;
// validation of fPercentValue
if (typeof (fPercentValue) == "number") {
if (that.getPercentValue() != fPercentValue) {
// animation without rerendering
this.$().addClass("sapMPIAnimate");
//var time = Math.abs(that.getPercentValue() - fPercentValue) * 20;
var time = 0;
this.setProperty("percentValue", fPercentValue, true);
var $Bar = this.$("bar");
$Bar.animate({
width : fPercentValue + "%"
}, time, "linear", function() {
that._setText.apply(that);
that.$().removeClass("sapMPIAnimate");
});
}
return this;
};

There is no convenient method to suppress this behavior.
You can only extend the control and overwrite the method setPercentValue to you desired behavior.

As of UI5 1.73, the animation on percantageValue-change can be turned off by setting the property displayAnimation to false.
Determines whether a percentage change is displayed with animation.
Since: 1.73.
<ProgressIndicator displayAnimation="false" />

Related

Display values outside of pie chart in chartjs

When I hover on pie chart, the values are displayed in tooltip. However, I want to display values outside of pie chart. I want to make chart like this image:
How to do this?
I was able to get something similar working using chart.js v2.3.0 using both the plugin API and extending chart types API. You should be able to take this as a starting point and tweak it to your needs.
Here is how it looks after being rendered.
Note, this requires digging deep into chart.js internals and could break if they change the way tooltips are positioned or rendered in the future. I also added a new configuration option called showAllTooltips to enable selectively using the plugin on certain charts. This should work for all chart types, but I am currently only using it for pie, doughnut, bar, and line charts so far.
With that said, here is a working solution for the image above.
Chart.plugins.register({
beforeRender: function (chart) {
if (chart.config.options.showAllTooltips) {
// create a namespace to persist plugin state (which unfortunately we have to do)
if (!chart.showAllTooltipsPlugin) {
chart.showAllTooltipsPlugin = {};
}
// turn off normal tooltips in case it was also enabled (which is the global default)
chart.options.tooltips.enabled = false;
// we can't use the chart tooltip because there is only one tooltip per chart which gets
// re-positioned via animation steps.....so let's create a place to hold our tooltips
chart.showAllTooltipsPlugin.tooltipsCollection = [];
// create a tooltip for each plot on the chart
chart.config.data.datasets.forEach(function (dataset, i) {
chart.getDatasetMeta(i).data.forEach(function (sector, j) {
// but only create one for pie and doughnut charts if the plot is large enough to even see
if (!_.contains(['doughnut', 'pie'], sector._chart.config.type) || sector._model.circumference > 0.1) {
var tooltip;
// create a new tooltip based upon configuration
if (chart.config.options.showAllTooltips.extendOut) {
// this tooltip reverses the location of the carets from the default
tooltip = new Chart.TooltipReversed({
_chart: chart.chart,
_chartInstance: chart,
_data: chart.data,
_options: chart.options.tooltips,
_active: [sector]
}, chart);
} else {
tooltip = new Chart.Tooltip({
_chart: chart.chart,
_chartInstance: chart,
_data: chart.data,
_options: chart.options.tooltips,
_active: [sector]
}, chart);
}
// might as well initialize this now...it would be a waste to do it once we are looping over our tooltips
tooltip.initialize();
// save the tooltips so they can be rendered later
chart.showAllTooltipsPlugin.tooltipsCollection.push(tooltip);
}
});
});
}
},
afterDraw: function (chart, easing) {
if (chart.config.options.showAllTooltips) {
// we want to wait until everything on the chart has been rendered before showing the
// tooltips for the first time...otherwise it looks weird
if (!chart.showAllTooltipsPlugin.initialRenderComplete) {
// still animating until easing === 1
if (easing !== 1) {
return;
}
// animation is complete, let's remember that fact
chart.showAllTooltipsPlugin.initialRenderComplete = true;
}
// at this point the chart has been fully rendered for the first time so start rendering tooltips
Chart.helpers.each(chart.showAllTooltipsPlugin.tooltipsCollection, function (tooltip) {
// create a namespace to persist plugin state within this tooltip (which unfortunately we have to do)
if (!tooltip.showAllTooltipsPlugin) {
tooltip.showAllTooltipsPlugin = {};
}
// re-enable this tooltip otherise it won't be drawn (remember we disabled all tooltips in beforeRender)
tooltip._options.enabled = true;
// perform standard tooltip setup (which determines it's alignment and x, y coordinates)
tooltip.update(); // determines alignment/position and stores in _view
tooltip.pivot(); // we don't actually need this since we are not animating tooltips, but let's be consistent
tooltip.transition(easing).draw(); // render and animate the tooltip
// disable this tooltip in case something else tries to do something with it later
tooltip._options.enabled = false;
});
}
},
});
// A 'reversed' tooltip places the caret on the opposite side from the current default.
// In order to do this we just need to change the 'alignment' logic
Chart.TooltipReversed = Chart.Tooltip.extend({
// Note: tooltipSize is the size of the box (not including the caret)
determineAlignment: function(tooltipSize) {
var me = this;
var model = me._model;
var chart = me._chart;
var chartArea = me._chartInstance.chartArea;
// set caret position to top or bottom if tooltip y position will extend outsite the chart top/bottom
if (model.y < tooltipSize.height) {
model.yAlign = 'top';
} else if (model.y > (chart.height - tooltipSize.height)) {
model.yAlign = 'bottom';
}
var leftAlign, rightAlign; // functions to determine left, right alignment
var overflowLeft, overflowRight; // functions to determine if left/right alignment causes tooltip to go outside chart
var yAlign; // function to get the y alignment if the tooltip goes outside of the left or right edges
var midX = (chartArea.left + chartArea.right) / 2;
var midY = (chartArea.top + chartArea.bottom) / 2;
if (model.yAlign === 'center') {
leftAlign = function(x) {
return x >= midX;
};
rightAlign = function(x) {
return x < midX;
};
} else {
leftAlign = function(x) {
return x <= (tooltipSize.width / 2);
};
rightAlign = function(x) {
return x >= (chart.width - (tooltipSize.width / 2));
};
}
overflowLeft = function(x) {
return x - tooltipSize.width < 0;
};
overflowRight = function(x) {
return x + tooltipSize.width > chart.width;
};
yAlign = function(y) {
return y <= midY ? 'bottom' : 'top';
};
if (leftAlign(model.x)) {
model.xAlign = 'left';
// Is tooltip too wide and goes over the right side of the chart.?
if (overflowLeft(model.x)) {
model.xAlign = 'center';
model.yAlign = yAlign(model.y);
}
} else if (rightAlign(model.x)) {
model.xAlign = 'right';
// Is tooltip too wide and goes outside left edge of canvas?
if (overflowRight(model.x)) {
model.xAlign = 'center';
model.yAlign = yAlign(model.y);
}
}
}
});

Ionic Decrease Scroll Speed

i got problem when trying to slowing scroll from this code :
$ionicScrollDelegate.$getByHandle('credit').scrollBottom(true).
How can i slowing down the scroll? Because now it scrolling too fast for me. I need to slowing down the scroll, just like credit scene on the Star Wars movie.
Anyhelp would be much appreciated, thanks!
$scope.viewCreditsV2 = function () {
$ionicModal.fromTemplateUrl('views/popupcredit.html', {
scope: $scope,
animation: 'slide-in-up'
}).then(function(modal) {
$scope.modal = modal;
$scope.modal.show();
if ($scope.modal.isShown()){
setTimeout(function() {
// Do something after 2 seconds
$ionicScrollDelegate.$getByHandle('credit').scrollBottom(true);
}, 2000);
}
});
$scope.openModal = function() {
$scope.modal.show();
};
$scope.closeModal = function() {
// $scope.modal.hide();
$scope.modal.remove();
};
};
This question is old but somebody might use it.
Even though there are no parameters to pass options, you can still access the ScrollView object using the $ionScrollDelegate.
Following #Jeremy Wilken's answer (which helped me derive this one), you could do:
$timeout(function() {
$ionicScrollDelegate.getScrollView().options.animationDuration = 400;
console.log($ionicScrollDelegate.getScrollView().options);
});
//.....
$ionicScrollDelegate.scrollBy(0,20, true) // Animation will be slower now
I wrapped the call on a $timeout to avoid racing conditions from $ionicScrollDelegate not being created.
Ionic doesn't have a means to change the animation speed for the $ionicScrollDelegate. There is no public API to make this change.
https://github.com/driftyco/ionic/blob/master/js/views/scrollView.js#L327
You can use $anchorScroll as shown in the Angular documentation https://docs.angularjs.org/api/ng/service/$anchorScroll

How to define cycles with observables

I'm trying to set up the update loop of a simple game, built with observables in mind. The top-level components are a model, which takes input commands, and produces updates; and a view, which displays the received updates, and produces input. In isolation, both work fine, the problematic part is putting the two together, since both depend on the other.
With the components being simplified to the following:
var view = function (updates) {
return Rx.Observable.fromArray([1,2,3]);
};
var model = function (inputs) {
return inputs.map(function (i) { return i * 10; });
};
The way I've hooked things together is this:
var inputBuffer = new Rx.Subject();
var updates = model(inputBuffer);
var inputs = view(updates);
updates.subscribe(
function (i) { console.log(i); },
function (e) { console.log("Error: " + e); },
function () { console.log("Completed"); }
);
inputs.subscribe(inputBuffer);
That is, I add a subject as a placeholder for the input stream, and attach the model to that. Then, after the view is constructed, I pass on the actual inputs to the placeholder subject, thus closing the loop.
I can't help but feel this is not the proper way to do things, however. Using a subject for this seems to be overkill. Is there a way to do the same thing with publish() or defer() or something along those lines?
UPDATE: Here's a less abstract example to illustrate what I'm having problems with. Below you see the code for a simple "game", where the player needs to click on a target to hit it. The target can either appear on the left or on the right, and whenever it is hit, it switches to the other side. Seems simple enough, but I still have the feeling I'm missing something...
//-- Helper methods and whatnot
// Variables to easily represent the two states of the target
var left = 'left';
var right = 'right';
// Transition from one side to the other
var flip = function (side) {
if (side === left) {
return right;
} else {
return left;
}
};
// Creates a predicate used for hit testing in the view
var nearby = function (target, radius) {
return function (position) {
var min = target - radius;
var max = target + radius;
return position >= min && position <= max;
};
};
// Same as Observable.prototype.scan, but it also yields the initial value immediately.
var initScan = function (values, init, updater) {
var initValue = Rx.Observable.return(init);
var restValues = values.scan(init, updater);
return initValue.concat(restValues);
};
//-- Part 1: From input to state --
var process = function (inputs) {
// Determine new state based on current state and input
var update = function(current, input) {
// Input value ignored here because there's only one possible state transition
return flip(current);
};
return initScan(inputs, left, update);
};
//-- Part 2: From display to inputs --
var display = function (states) {
// Simulate clicks from the user at various positions (only one dimension, for simplicity)
var clicks = Rx.Observable.interval(800)
.map(function (v) {return (v * 5) % 30; })
.do(function (v) { console.log("Shooting at: " + v)})
.publish();
clicks.connect();
// Display position of target depending on the model
var targetPos = states.map(function (state) {
return state === left ? 5 : 25;
});
// Determine which clicks are hits based on displayed position
return targetPos.flatMapLatest(function (target) {
return clicks
.filter(nearby(target, 10))
.map(function (pos) { return "HIT! (# "+ pos +")"; })
.do(console.log);
});
};
//-- Part 3: Putting the loop together
/**
* Creates the following feedback loop:
* - Commands are passed to the process function to generate updates.
* - Updates are passed to the display function to generates further commands.
* - (this closes the loop)
*/
var feedback = function (process, display) {
var inputBuffer = new Rx.Subject(),
updates = process(inputBuffer),
inputs = display(updates);
inputs.subscribe(inputBuffer);
};
feedback(process, display);
I think I understand what you are trying to achieve here:
How can I get a sequence of input events going in one direction that feed into a model
But have a sequence of output events going in the other direction that feed from the model to the view
I believe the answer here is that you probably want to flip your design. Assuming an MVVM style design, instead of having the Model know about the input sequence, it becomes agnostic. This means that you now have a model that has a InputRecieved/OnInput/ExecuteCommand method that the View will call with the input values. This should now be a lot easier for you to deal with a "Commands in one direction" and "Events in the other direction" pattern. A sort of tip-of-the-hat to CQRS here.
We use that style extensively on Views+Models in WPF/Silverlight/JS for the last 4 years.
Maybe something like this;
var model = function()
{
var self = this;
self.output = //Create observable sequence here
self.filter = function(input) {
//peform some command with input here
};
}
var viewModel = function (model) {
var self = this;
self.filterText = ko.observable('');
self.items = ko.observableArray();
self.filterText.subscribe(function(newFilterText) {
model.filter(newFilterText);
});
model.output.subscribe(item=>items.push(item));
};
update
Thanks for posting a full sample. It looks good. I like your new initScan operator, seems an obvious omission from Rx.
I took your code an restructured it the way I probably would have written it. I hope it help. The main things I did was encapsulted the logic into the model (flip, nearby etc) and have the view take the model as a parameter. Then I did also have to add some members to the model instead of it just being an observable sequence. This did however allow me to remove some extra logic from the view and put it in the model too (Hit logic)
//-- Helper methods and whatnot
// Same as Observable.prototype.scan, but it also yields the initial value immediately.
var initScan = function (values, init, updater) {
var initValue = Rx.Observable.return(init);
var restValues = values.scan(init, updater);
return initValue.concat(restValues);
};
//-- Part 1: From input to state --
var process = function () {
var self = this;
var shots = new Rx.Subject();
// Variables to easily represent the two states of the target
var left = 'left';
var right = 'right';
// Transition from one side to the other
var flip = function (side) {
if (side === left) {
return right;
} else {
return left;
}
};
// Determine new state based on current state and input
var update = function(current, input) {
// Input value ignored here because there's only one possible state transition
return flip(current);
};
// Creates a predicate used for hit testing in the view
var isNearby = function (target, radius) {
return function (position) {
var min = target - radius;
var max = target + radius;
return position >= min && position <= max;
};
};
self.shoot = function(input) {
shots.onNext(input);
};
self.positions = initScan(shots, left, update).map(function (state) {
return state === left ? 5 : 25;
});
self.hits = self.positions.flatMapLatest(function (target) {
return shots.filter(isNearby(target, 10));
});
};
//-- Part 2: From display to inputs --
var display = function (model) {
// Simulate clicks from the user at various positions (only one dimension, for simplicity)
var clicks = Rx.Observable.interval(800)
.map(function (v) {return (v * 5) % 30; })
.do(function (v) { console.log("Shooting at: " + v)})
.publish();
clicks.connect();
model.hits.subscribe(function(pos)=>{console.log("HIT! (# "+ pos +")");});
// Determine which clicks are hits based on displayed position
model.positions(function (target) {
return clicks
.subscribe(pos=>{
console.log("Shooting at " + pos + ")");
model.shoot(pos)
});
});
};
//-- Part 3: Putting the loop together
/**
* Creates the following feedback loop:
* - Commands are passed to the process function to generate updates.
* - Updates are passed to the display function to generates further commands.
* - (this closes the loop)
*/
var feedback = function (process, display) {
var model = process();
var view = display(model);
};
feedback(process, display);
I presume that because you do not "assign" the inputs after the model is created, you are aiming for a non-mutative approach to instantiating your model and view. However, your model and your view seem to depend on one another. To resolve this issue, you can use a third party to facilitate the relationship between the two objects. In this case, you can simply use a function for dependency injection...
var log = console.log.bind(console),
logError = console.log.bind(console, 'Error:'),
logCompleted = console.log.bind(console, 'Completed.'),
model(
function (updates) {
return view(updates);
}
)
.subscribe(
log,
logError,
logCompleted
);
By providing the model a factory to create a view, you give the model the ability to fully instantiate itself by instantiating it's view, but without knowing how the view is instantiated.
As per my comment on the question itself, here's the same sort of code you're writing done with a scheduler in Windows. I would expect a similar interface in RxJS.
var scheduler = new EventLoopScheduler();
var subscription = scheduler.Schedule(
new int[] { 1, 2, 3 },
TimeSpan.FromSeconds(1.0),
(xs, a) => a(
xs
.Do(x => Console.WriteLine(x))
.Select(x => x * 10)
.ToArray(),
TimeSpan.FromSeconds(1.0)));
The output I get, with three new numbers every second, is:
1
2
3
10
20
30
100
200
300
1000
2000
3000
10000
20000
30000

popup fixed it position

I am having a popup and it position is set dynamically according to the screen but when the number of the label increase the popup create problem as ones its show in upper side then when the label goes top on the scolling window it suppose to come down but its not coming down.
var position= popup.offsetTop;
alert("popupTop"+position);
var positionpop=popup.offsetHeight;
var width1=label.offsetWidth;
var position1=label.offsetHeight;
var position2=label.offsetTop;
var windowHeight = $wnd.innerHeight;
var scroll=$wnd.pageYOffset;
alert(scroll+"scroll");
var t=position-scroll;
var z=windowHeight-scroll;
var x="Totalheight"+screen.height;
var y=position1+position2+positionpop;
if(position1+position2+positionpop<windowHeight)
{
alert("no change");
}
if(position1+position2+positionpop>windowHeight )
{
popup.setAttribute("style","top:"+(position2-positionpop)+"px");
}
if(t<=0 || z<=0)
{
popup.setAttribute("style","bottom:"+(diff)+"px");
}
I have reached upto the dynamically changing the position up and down when the label is in the top but still have one problem that I both up and down position occured one by one I want to bound it into one..
if(position2+positionpop<windowHeight || t<=0)
{
popup.setAttribute("style","bottom:"+((windowHeight-(position2+position1))-positionpop)+"px");
}
else
{
if(position2+positionpop>windowHeight && t>0)
{
popup.setAttribute("style","top:"+(position2-positionpop)+"px");
}
}
You should use PopupPanel.setPopupPositionAndShow and PopupPanel.setPopupPosition. See the PopupPanel javadoc sample.

is there an alternative to DOMAttrModified that will work in webkit

I need to leverage this DOM event. IE has onpropertychange, which does what I need it to do also. Webkit doesn't seem to support this event, however. Is there an alternative I could use?
Although Chrome does not dispatch DOMAttrModified events, the more lightweighted mutation observers are supported since 2011 and these work for attribute changes, too.
Here is an example for the document body:
var element = document.body, bubbles = false;
var observer = new WebKitMutationObserver(function (mutations) {
mutations.forEach(attrModified);
});
observer.observe(element, { attributes: true, subtree: bubbles });
function attrModified(mutation) {
var name = mutation.attributeName,
newValue = mutation.target.getAttribute(name),
oldValue = mutation.oldValue;
console.log(name, newValue, oldValue);
}
For a simple attribute change, the console.log statement would print:
<body color="black">
<script type="text/html">
document.body.setAttribute("color", "red");
</script>
</body>
Console:
> color red black
If you are happy with merely detecting calls to setAttribute() (as opposed to monitoring all attribute modifications) then you could over-ride that method on all elements with:
Element.prototype._setAttribute = Element.prototype.setAttribute
Element.prototype.setAttribute = function(name, val) {
var e = document.createEvent("MutationEvents");
var prev = this.getAttribute(name);
this._setAttribute(name, val);
e.initMutationEvent("DOMAttrModified", true, true, null, prev, val, name, 2);
this.dispatchEvent(e);
}
I had the same question and was thinking of modifying setAttribute, so seeing what Sean did, I copied that. Worked great, except that it was firing when an attribute was repeatedly set to the same value, so I added a check to my copy to skip firing the event if the value is not being changed. I also added val = String(val), based on the rationale that setAttribute will coerce numbers to strings, so the comparison should anticipate that.
My modified version is:
var emulateDOMAttrModified = {
isSupportedNatively: function () {
var supported = false;
function handler() {
supported = true;
}
document.addEventListener('DOMAttrModified', handler);
var attr = 'emulateDOMAttrModifiedTEST';
document.body.setAttribute(attr, 'foo'); // aka $('body').attr(attr, 'foo');
document.removeEventListener('DOMAttrModified', handler);
document.body.removeAttribute(attr);
return supported;
},
install: function () {
if (!this.isSupportedNatively() &&
!Element.prototype._setAttribute_before_emulateDOMAttrModified) {
Element.prototype._setAttribute_before_emulateDOMAttrModified = Element.prototype.setAttribute
Element.prototype.setAttribute = function(name, val) {
var prev = this.getAttribute(name);
val = String(val); /* since attributes do type coercion to strings,
do type coercion here too; in particular, D3 animations set x and y to a number. */
if (prev !== val) {
this._setAttribute_before_emulateDOMAttrModified(name, val);
var e = document.createEvent('MutationEvents');
e.initMutationEvent('DOMAttrModified', true, true, null, prev, val, name, 2);
this.dispatchEvent(e);
}
};
}
}
};
// Install this when loaded. No other file needs to reference this; it will just make Chrome and Safari
// support the standard same as Firefox does.
emulateDOMAttrModified.install();
Please refer code:
https://github.com/meetselva/attrchange/blob/master/attrchange.js
'DOMAttrModified' + ('propertychange' for IE) are used there like in your case. If it's not suitable for you, the "ugly" solution that can satisfy this demand should be setInterval(function(){}, delay)
Otherwise see Sean Hogan post above.
The solution provided by #Filip is close (and may have worked at the time) but now you need to request delivery of the old attribute value.
Thus, you'll want to change :
observer.observe(element, { attributes: true, subtree: bubbles });
to this:
observer.observe(element, { attributes: true, attributeOldvalue:true, subtree: bubbles });
Otherwise, you won't see the oldValues (you'll get null instead.) This was tested in Chrome 34.0.1847.131 (Official Build 265687) m.