So i have an image that i want to drop down the page.
Should the user click a button, the image will stop said dropping down the page.
I've used the eventListener 'complete' style to execute this... and it works, in a fashion. The problem is that the dropping down is choppy ~ irritatingly so.
Is there a more efficient way for titanium to do some form of simple animation?
Here is a code slice:
ballAnimation = Ti.UI.createAnimation({
top: ballDown.top + 0.01*heightOfScreen,
duration: someSpeedHere
}, function(){
if (hasBeenPressed){
return;
}
else if (!hasBeenPressed && ballAnimation.top > lowestPointForBall){
someFunctionHere(); //this isn't part of the problem.
}
}
);
ballAnimation.addEventListener('complete', function(){
if (hasBeenPressed){
return;
}
else if (!hasBeenPressed && ballAnimation.top > lowestPointForBall){
someFunctionHere(); //this isn't part of the problem.
} else {
ballAnimation.top = ballAnimation.top + 0.01*heightOfScreen;
ballDown.animate(ballAnimation);
}
});
ballDown.animate(ballAnimation);
For animations it is advised to use a 2D matrix with a translation like this:
var translation = Titanium.UI.create2DMatrix(), deltaX, deltaY; // set the deltaX and deltaY according
translation = translation.translate(deltaX, deltaY);
ballDown.animate({
transform : translation,
duration : someSpeedHere
});
Related
I have used this solution to get a toggle legend for a composite line chart and it works perfectly fine.
However, after i added a range chart to this composite chart, the deselected legend loses its translucence and becomes normal.
How can i keep the deselected legend object in faded state while filtering?
Here are screenshots for reference:
Before filter:
After filter:
This is the code I'm using for charts:
multiLineChart
.width(1000)
.height(300)
.transitionDuration(1000)
.margins({top: 30, right: 50, bottom: 40, left: 40})
.x(d3.time.scale().domain([startDate,endDate]))
.yAxisLabel("Data (Scaled)")
.xAxisLabel("Date And Time")
.rangeChart(timeSlider)
.legend(dc.legend().x(800).y(20).itemHeight(13).gap(5))
.renderHorizontalGridLines(true)
//.dimension(DateDim)
.compose([
dc.lineChart(multiLineChart)
.dimension(DateDim)
.colors('red')
.group(Line1Grp, 'Line1'),
dc.lineChart(multiLineChart)
.dimension(DateDim)
.colors('blue')
.group(Line2Grp, 'Line2')
])
.brushOn(false)
.on('pretransition.hideshow', function(chart) {
chart.selectAll('g.dc-legend .dc-legend-item')
.on('click.hideshow', function(d, i) {
var subchart = chart.select('g.sub._' + i);
var visible = subchart.style('visibility') !== 'hidden';
subchart.style('visibility', function() {
return visible ? 'hidden' : 'visible';
});
d3.select(this).style('opacity', visible ? 0.2 : 1);
});
});
//.xAxis().tickFormat(d3.time.format("%b %d %H:%M"));
timeSlider
.width(1000)
.height(50)
.margins({top: 0, right: 50, bottom: 20, left: 40})
.dimension(DateDim)
.group(Line1Grp)
.x(d3.time.scale().domain([startDate, endDate]))
.on("filtered", function (chart) {
dc.events.trigger(function () {
multiLineChart.focus(chart.filter());
dc.redrawAll(chart.chartGroup());
});
})
.xAxis().tickFormat(d3.time.format("%b %d"));
Here is a fiddle for the same.
Any help is appreciated.
Thanks for pointing this out - there was a bad practice in my earlier answer, and I went back and corrected it.
It's always better style, and more robust, to separate event handling and drawing, and always draw everything based on the data, not some event that is in flight.
If you follow these practices, then the code looks more like this:
function drawLegendToggles(chart) {
chart.selectAll('g.dc-legend .dc-legend-item')
.style('opacity', function(d, i) {
var subchart = chart.select('g.sub._' + i);
var visible = subchart.style('visibility') !== 'hidden';
return visible ? 1 : 0.2;
});
}
function legendToggle(chart) {
chart.selectAll('g.dc-legend .dc-legend-item')
.on('click.hideshow', function(d, i) {
var subchart = chart.select('g.sub._' + i);
var visible = subchart.style('visibility') !== 'hidden';
subchart.style('visibility', function() {
return visible ? 'hidden' : 'visible';
});
drawLegendToggles(chart);
})
drawLegendToggles(chart);
}
multiLineChart
.on('pretransition.hideshow', legendToggle);
Now, whenever we redraw the composite chart and its legend - no matter what the cause - all of the items in the legend will be updated based on whether the corresponding child chart has been hidden.
And the event handler is only concerned with hiding and showing charts, not drawing.
Fork of your fiddle.
I want to create an "image comparison slider" for contents. Example:
Codepen Link
// Call & init
$(document).ready(function(){
$('.ba-slider').each(function(){
var cur = $(this);
// Adjust the slider
var width = cur.width()+'px';
cur.find('.resize img').css('width', width);
// Bind dragging events
drags(cur.find('.handle'), cur.find('.resize'), cur);
});
});
// Update sliders on resize.
// Because we all do this: i.imgur.com/YkbaV.gif
$(window).resize(function(){
$('.ba-slider').each(function(){
var cur = $(this);
var width = cur.width()+'px';
cur.find('.resize img').css('width', width);
});
});
function drags(dragElement, resizeElement, container) {
// Initialize the dragging event on mousedown.
dragElement.on('mousedown touchstart', function(e) {
dragElement.addClass('draggable');
resizeElement.addClass('resizable');
// Check if it's a mouse or touch event and pass along the correct value
var startX = (e.pageX) ? e.pageX : e.originalEvent.touches[0].pageX;
// Get the initial position
var dragWidth = dragElement.outerWidth(),
posX = dragElement.offset().left + dragWidth - startX,
containerOffset = container.offset().left,
containerWidth = container.outerWidth();
// Set limits
minLeft = containerOffset + 10;
maxLeft = containerOffset + containerWidth - dragWidth - 10;
// Calculate the dragging distance on mousemove.
dragElement.parents().on("mousemove touchmove", function(e) {
// Check if it's a mouse or touch event and pass along the correct value
var moveX = (e.pageX) ? e.pageX : e.originalEvent.touches[0].pageX;
leftValue = moveX + posX - dragWidth;
// Prevent going off limits
if ( leftValue < minLeft) {
leftValue = minLeft;
} else if (leftValue > maxLeft) {
leftValue = maxLeft;
}
// Translate the handle's left value to masked divs width.
widthValue = (leftValue + dragWidth/2 - containerOffset)*100/containerWidth+'%';
// Set the new values for the slider and the handle.
// Bind mouseup events to stop dragging.
$('.draggable').css('left', widthValue).on('mouseup touchend touchcancel', function () {
$(this).removeClass('draggable');
resizeElement.removeClass('resizable');
});
$('.resizable').css('width', widthValue);
}).on('mouseup touchend touchcancel', function(){
dragElement.removeClass('draggable');
resizeElement.removeClass('resizable');
});
e.preventDefault();
}).on('mouseup touchend touchcancel', function(e){
dragElement.removeClass('draggable');
resizeElement.removeClass('resizable');
});
}
Same as above, but instead of images, I want to create content divs with text, images, html. Like 2 pages with a slider to compare both.
Your comparison script utilizes a resizing a div, so there's no way (that I know of) to achieve this without some considerable trickery (or how to achieve the same effect without resizing).
The only way to prevent the text from automatically wrapping is by using ...
white-space: nowrap;
... but that would just result in one continuous line of text.
Breaks <br> could be inserted from there, but that would just look awful and negate any resizing.
Personally, my approach would be to render the text within a canvas element first (https://www.w3schools.com/graphics/canvas_text.asp).
Since a canvas is treated nearly identical to that of an image, it wouldn't require any changes to the existing comparison script.
My complete source code for this issue is posted as an Expo app: https://exp.host/#kevinoldlifeway/swipe-scrollview-of-webviews as well as on Github https://github.com/kevinold/swipe-scrollview-of-webviews
I am building a book view in React Native. Using a ScrollView, I would like to swipe left and right to navigate through the pages of a title that could have several hundred to several thousand.
Since that is the case, my goal is to only the minimal amount of data so that the user is able to swipe between pages, seeing the immediately previous and next pages.
I am loading a 3 element array like so:
[Previous, Current, Next]
That would be updated in the state by Redux (not used here to keep simple) and would re-render and refocus the list.
My goal is that my ScrollView is always "centered" on the "Current" page.
Page scrolls to the previous and next page are handled by a handleScroll method which loads the appropriate precomputed array so that the current page stays in focus, but the previous and next pages (offscreen) are updated appropriately.
handleScroll (event) {
//const x = event.nativeEvent.contentOffset.x;
const { activeIndex, scrollTimes } = this.state;
const windowWidth = Dimensions.get('window').width;
const eventWidth = event.nativeEvent.contentSize.width;
const offset = event.nativeEvent.contentOffset.x;
console.log('event width: ', eventWidth);
console.log('event offset: ', offset);
console.log('scrollTimes: ', scrollTimes);
//if (scrollTimes <= 1 ) return;
if (windowWidth + offset >= eventWidth) {
//ScrollEnd, do sth...
console.log('scrollEnd right (nextPage)', offset);
const nextIndex = activeIndex + 1;
console.log('nextIndex: ', nextIndex);
// Load next page
this.loadMore()
} else if (windowWidth - offset <= eventWidth) {
//ScrollEnd, do sth...
console.log('scrollEnd left (prevPage)', offset);
// Load prev page
this.loadPrev()
}
this.setState({ scrollTimes: scrollTimes + 1 });
}
I have tried to balance the "current" page using a combination of:
contentOffset={{ x: width, y: 0 }} on ScrollView
And
componentDidMount() {
// Attempt to keep "center" element in array as focused "screen" in the horizontal list view
this.scrollView.scrollTo({ x: width, y: 0, animated: false });
}
I've also tried to scrollTo in the callback after this.setState, but have not had any luck.
I'm wondering if this "centering" could be accomplished by using Animated.
I gave this a shot but I'm not entirely sure I understood the problem, and I'm not sure how well this would hold up.
Basically I just simplified the handleScroll function significantly. First checking if we were on a scroll completion and if so determining if when we landed on that screen it was the "previous" screen or "next" - do nothing if it's already the middle screen.
I think in your code the issue was that it would fire and load data if it was the middle screen, not just the first or last. Therefore it would fire twice for each transition.
Here's the handleScroll that I think will work for you.
handleScroll (event) {
const offset = event.nativeEvent.contentOffset.x;
const mod = offset % width;
if (mod === 0) { // only transition on scroll complete
if (offset === width * 2) { // last screen
console.log('load more')
this.loadMore();
this.scrollView.scrollTo({ x: width, y: 0, animated: false });
} else if (offset !== width) { // first screen
console.log('load prev')
this.loadPrev();
this.scrollView.scrollTo({ x: width, y: 0, animated: false });
}
}
}
And a Snack demoing it.
I have an image slider that is controlled by text navigation. The text is highlighted orange when it's relative slide is current in the gallery. I would like the other text to have an inactive state with a black background but cannot get this to work!
(In case that didn't make much sense! Basically, I want background-color orange when current, background-color black when inactive.) THANKS
$(document).ready(function(){
$('.slider').each(function(e){
if(e == 0){
$(this).addClass('current');
}
$(this).attr('id', 'handle' + e);
})
$('.tabs li').each(function(e){
if(e == 0){
$(this).addClass('current'); //adds class current to 1st li
}
$(this).wrapInner('<a class="title"></a>'); //wraps list items in anchor tag
$(this).children('a').attr('href', '#handle' + e);//adds href to the anchors
t = $(this).children('a').text();
$('#handle' + e).append('<h2>' + t + '</h2>'); //adds h2 and text to big images
})
$('.tabs li a').click(function(){
c = $(this).attr('href');
if($(c).hasClass('current')){
return false;
}else{
showImage($(c), 20);
$('.tabs li').removeClass('current');
$(this).parent().addClass('current');
return false;
}
})
runRotateImages();
$("#featured").hover(
function(){
clearTimeout(xx);
},
function(){
runRotateImages();
}
)
})
function showImage(img, duration){
$('.slider').removeClass('current').css({
"opacity" : 0.0,
"zIndex" : 2
});
img.animate({opacity:1.0}, duration, function(){
$(this).addClass('current').css({zIndex:1});
});
}
function rotateImages(){
var curPhoto = $("div.current");
var nxtPhoto = curPhoto.next();
var curTab = $(".tabs li.current");
var nxtTab = curTab.next();
if (nxtPhoto.length == 0) {
nxtPhoto = $('#featured div:first');
nxtTab = $('.tabs li:first-child');
}
curTab.removeClass('current');
nxtTab.addClass('current');
showImage(nxtPhoto, 300);
}
function runRotateImages(){
xx = setInterval("rotateImages()", 5000);
}
I have added a jsfiddle - http://jsfiddle.net/n5EPM/3/
However, on jsfiddle it does not seem to automatically cycle through the images, not sure why, have no problems in browser.
Try using not() method: http://api.jquery.com/not/
Basically, you need to create a new class disabled
.disabled{
background-color:#000000;
}
Then, add the following line to your tabs.li's each loop:
$(".tabs li").not(".current").addClass('disabled'); //add disabled class for non-current tabs
At last you need to remove disabled class in the rotateimage() function before assigning current and then disable non-current again. like this:
curTab.removeClass('current');
nxtTab.removeClass('disabled'); //remove diabled class
nxtTab.addClass('current');
$(".tabs li").not(".current").addClass('disabled'); // disable non-current again
Working jsfiddle here: http://jsfiddle.net/n5EPM/9/
This might not be the perfect solution but you will need to tweak it a little bit.
Hope this helps.
I would like to load a few images to the Image object in QML. I want to do it one after another in a loop I think with some delay, because it should pretend to look like an animation. Im new in Qt and QML, so can anybody help me how to begin or somethin'? :)
The simplest solution, that works in QML 1.x and 2.0, is to use a Repeater and a Timer :
import QtQuick 2.0
Rectangle {
id: base;
width: 800;
height: 600;
property variant images : [
"image1.jpg",
"image2.jpg",
"image3.jpg",
"image4.jpg",
"image5.jpg"
];
property int currentImage : 0;
Repeater {
id: repeaterImg;
model: images;
delegate: Image {
source: modelData;
asynchronous: true;
visible: (model.index === currentImage);
}
}
Timer {
id: timerAnimImg:
interval: 500; // here is the delay between 2 images in msecs
running: false; // stopped by default, use start() or running=true to launch
repeat: true;
onTriggered: {
if (currentImage < images.length -1) {
currentImage++; // show next image
}
else {
currentImage = 0; // go back to the first image at the end
}
}
}
}
Should do it, and if you don't want the animation to restart when the last image is reached, just replace currentImage = 0; with stop(); in the timer's onTriggered.
Also, you should possibly have to tweak a little the Image delegate in the Repeater to give it the look you want (size, fill mode, position etc...).
If you are using QtQuick 2 then AnimatedSprite is the best way to do this: http://qt-project.org/doc/qt-5.0/qtquick/qtquick-effects-sprites.html
Otherwise, you could use a Timer or NumberAnimation to trigger changing the image source, but there could be unpredictable delays as images load for the first time (caching should solve this after the first loop). If you only have a few images, you could have three Images and cycle their visibility.