I've built this isotope module - but I want to enhance it - so it snaps to the bottom, and can take updates from a json request. Also if a change occurs - like an online user views a profile - to make the change to a personal reference on the user's page.
http://jsfiddle.net/CXqM2/26/
Here is the current code
var $container = $( '#isotope' ),
// #see {#link http://fgnass.github.io/spin.js}
spinJsConfiguration = {
lines: 5, // The number of lines to draw
length: 3, // The length of each line
width: 2, // The line thickness
radius: 6, // The radius of the inner circle
color: '#666' // #rgb or #rrggbb or array of colors
};
// initialize isotope
// prevent "First item breaks Masonry layout" issue
// #see {#link http://isotope.metafizzy.co/docs/help.html#first_item_breaks_masonry_layout}
$container.isotope({
masonry: {
columnWidth: 30
}
});
// handle click events
$container.on( 'click', '.user', function( event ) {
var $this = $( this );
event.preventDefault();
// if not already open, do so
if ( !$this.hasClass( 'open' ) ){
var $openItem = $container.find( '.open' );
// if any, close currently open items
if ( $openItem.length ) {
closeItem( $openItem );
}
openItem( $this );
}
});
$container.on( 'click', '.close', function( event ) {
event.stopPropagation();
closeItem( $( this ).closest( '.user' ) );
});
function openItem( $item ) {
var $image = $item.find( '.user-image' );
$item.addClass( 'loading' ).spin( spinJsConfiguration );
// #todo we should only replace the image once
$image.attr( 'src', $image.data( 'src-large' ) );
// at least for the sake of this demo we can use the "imagesLoaded" plugin contained within
// Isotope to determine if the large version of the user image has loaded
// #todo Isotope v1 contains an outdated version of the "imagesLoaded" plugin - please use the current one
// #see {#link https://github.com/desandro/imagesloaded}
$item.imagesLoaded( function() {
$item.spin( false ).removeClass( 'loading' ).addClass( 'open' );
$container.addClass( 'item-open' ).isotope( 'reLayout' );
$item.append( '<div class="close">×</div>' );
});
}
function closeItem( $item ) {
$item.removeClass( 'open' ).find( '.close' ).remove();
$container.removeClass( 'item-open' ).isotope( 'reLayout' );
}
This demo
http://jsfiddle.net/CXqM2/85/
Is able to update the isotope with json data. I am able to re-populate the list with new json data updates.
I essentially want items no longer existing to fade off - remove
new items to be added on - add/insert
any priority updates like - user sent you a message to auto open on this for the user. How do I trigger this?
here is the code that repopulates every 10 seconds
getUpdate: function(){
function getRandomInt (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var that = this;
window.setInterval(function(){
console.log("new data time");
var rand = getRandomInt (0, 1);
that.populateIsotope(data[rand].stream);
//_re_invoke isotope
$container.isotope('reLayout')
},10000);
}
LATEST CODE ******** http://jsfiddle.net/CXqM2/209/ *******
This example will rely on some development on the backend to provide a priority element - using the notification date and the notification id to help identify the priority element in the stream. I've added the date sorting to the isotope code.
getSortData: {
date: function ($elem) {
var dates = $elem.attr('data-user-notification-date');
dateArray = dates.split('/'),
year = dateArray[2].substr(0, 4),
month = dateArray[1],
day = dateArray[0];
timeArray = dates.split(':'),
hours = timeArray[0].slice(-2),
minutes = timeArray[1],
seconds = timeArray[2];
return new Date(year, month, day, hours, minutes, seconds);
}
}
Related
How can i change a plugin's settings/options dynamically. In this case slider plugin. I am trying to change data-step.
Here what i tried:
function changeDataStep($val) {
var slider = new Foundation.Slider( $('#slider') );
$('#slider').foundation('destroy');
slider = new Foundation.Slider( $('#slider'), { dataStep: $val } );
}
Attributes which starts with data in javascript is the source of confusion.
Correct Way is:
function changeDataStep($val) {
var slider = new Foundation.Slider( $('#slider') );
slider = new Foundation.Slider( $('#slider'), { step: $val } );
}
I use the background.js script to create new windows, but when I set the bounds when using the chrome.app.window.create method and pass it parameters for the CreateWindowOptions object the are used the first time, but ignored whenever I relaunch the app.
On OSX, it seems that calling the methods setPosition and setSize after the create callback on the window's outerBounds object the app will then respond to the bounds I try to set. However, this doesn't appear to work on Windows 10 at all.
It seems somehow the windows I create persist when the app is closed. I'm not sure how to clear them, especially if the user closes the app uses the menu or OS to quit. Is there some way to make sure to destroy or clear these windows, or force the Chrome App to set the bounds I set without reverting to some previous state?
The windows I create are running PixiJS if that makes any difference. I do call a destroy method on the windows when the close event is triggered, but that has no affect.
Here's my background script:
(function( global ) {
"use strict";
/* ----- VARS ----- */
var displayInfo;
var isMultiDisplay;
var controlDisplayIndex;
var controlBounds;
var controlLoaded;
var controlWindow;
var gameDisplayIndex;
var gameBounds;
var gameLoaded;
var gameWindow;
/* ----- CONSTANTS ----- */
var CONTROL_WIDTH = 1920;
var CONTROL_HEIGHT = 1080;
var GAME_WIDTH = 2160;
var GAME_HEIGHT = 3840;
var FRAME_OPTIONS = {
type: "none"
};
/* ----- FUNCTIONS ----- */
function init() {
console.log( "background.js: init" );
console.info( "background.js: ", chrome.app.window.getAll() );
isMultiDisplay = false;
controlLoaded = false;
gameLoaded = false;
loadDisplayInfo();
};
function loadDisplayInfo() {
console.log( "background.js: loadDisplayInfo" );
chrome.system.display.getInfo( onDisplayInfo );
};
function createWindows() {
console.log( "background.js: createWindows" );
isMultiDisplay = ( displayInfo.length > 0 );
if ( isMultiDisplay ) {
var i = 0;
var length = 2;
for ( i; i < length; i++ ) {
if ( displayInfo[i].isPrimary )
controlDisplayIndex = i;
else
gameDisplayIndex = i;
}
gameBounds = {
height: displayInfo[ gameDisplayIndex ].workArea.height,
left: displayInfo[ gameDisplayIndex ].workArea.left,
top: displayInfo[ gameDisplayIndex ].workArea.top,
width: displayInfo[ gameDisplayIndex ].workArea.width
};
controlBounds = {
height: displayInfo[ controlDisplayIndex ].workArea.height,
left: displayInfo[ controlDisplayIndex ].workArea.left,
top: displayInfo[ controlDisplayIndex ].workArea.top,
width: displayInfo[ controlDisplayIndex ].workArea.width
};
} else {
// This assumes single monitor is in landscape.
gameBounds = {
top: displayInfo[0].workArea.top,
left: displayInfo[0].workArea.left,
height: displayInfo[0].workArea.height,
width: Math.floor( displayInfo[0].workArea.height * GAME_WIDTH / GAME_HEIGHT )
};
controlBounds = {
top: displayInfo[0].workArea.top,
left: displayInfo[0].workArea.left + gameBounds.width,
width: gameBounds.width,
height: Math.floor( gameBounds.width * CONTROL_HEIGHT / CONTROL_WIDTH )
};
}
console.info( "Game Bounds:", gameDisplayIndex, gameBounds.left, gameBounds.top, gameBounds.width, gameBounds.height );
console.info( "Control Bounds:", controlDisplayIndex, controlBounds.left, controlBounds.top, controlBounds.width, controlBounds.height );
var state = ( isMultiDisplay ) ? "fullscreen" : "normal";
// Game
chrome.app.window.create( "window-game.html", {
id: "gameWindow",
bounds: gameBounds,
frame: FRAME_OPTIONS,
resizable: true,
state: state
}, onGameWindowCreated );
// Control
chrome.app.window.create( "window-control.html", {
id: "controlWindow",
bounds: controlBounds,
frame: FRAME_OPTIONS,
resizable: true,
state: state
}, onControlWindowCreated );
};
/* ----- EVENT LISTENERS ----- */
function onLaunched() {
console.log( "background.js: onLaunched" );
init();
};
function onDisplayInfo( info ) {
console.log( "background.js: onDisplayInfo" );
displayInfo = info;
createWindows();
};
function onControlWindowCreated( obj ) {
console.log( "background.js: onControlWindowCreated", obj.id );
controlLoaded = true;
controlWindow = obj;
controlWindow.outerBounds.setPosition( controlBounds.left, controlBounds.top );
controlWindow.outerBounds.setSize( controlBounds.width, controlBounds.height );
if ( isMultiDisplay ) controlWindow.fullscreen();
//console.info( controlWindow.innerBounds.width, controlWindow.innerBounds.height );
//console.info( controlWindow.outerBounds.width, controlWindow.outerBounds.height );
controlWindow.onClosed.addListener( onControlWindowClosed );
};
function onControlWindowClosed() {
console.log( "background.js: onControlWindowClosed" );
controlWindow.onClosed.removeListener( onControlWindowClosed );
controlWindow.destroy();
controlWindow = undefined;
};
function onGameWindowCreated( obj ) {
console.log( "background.js: onGameWindowCreated", obj.id );
gameLoaded = true;
gameWindow = obj;
gameWindow.outerBounds.setPosition( gameBounds.left, gameBounds.top );
gameWindow.outerBounds.setSize( gameBounds.width, gameBounds.height );
if ( isMultiDisplay ) gameWindow.fullscreen();
//console.info( gameWindow.innerBounds.width, gameWindow.innerBounds.height );
//console.info( gameWindow.outerBounds.width, gameWindow.outerBounds.height );
gameWindow.onClosed.addListener( onGameWindowClosed );
};
function onGameWindowClosed() {
console.log( "background.js: onGameWindowClosed" );
gameWindow.onClosed.removeListener( onGameWindowClosed );
gameWindow.destroy();
gameWindow = undefined;
};
/* ----- CALL ----- */
chrome.app.runtime.onLaunched.addListener( onLaunched );
}( this ));
Disclaimer: I know that Chrome Apps are being discontinued, but due to project timings I've got to finish this app off and then look to port to Electron or alternative.
When you create a window and set the id property in the CreateWindowOptions object, the last known bounds for that window will be saved by the Chrome Apps platform and used the next a window is created with that same id.
Here's the description from the docs on the id property:
Id to identify the window. This will be used to remember the size and
position of the window and restore that geometry when a window with
the same id is later opened. If a window with a given id is created
while another window with the same id already exists, the currently
opened window will be focused instead of creating a new window.
And under the description of the outerBounds property:
Used to specify the initial position, initial size and constraints of
the window (including window decorations such as the title bar and
frame). If an id is also specified and a window with a matching id has
been shown before, the remembered bounds will be used instead.
So, the solution is to omit the id property. If you, like myself, need to do further things with that window in your script, then use the create window callback to assign a variable to that's function return parameter.
For example:
var myWindow;
chrome.app.window.create( "window.html", options, function( obj ) {
myWindow = obj;
// Do Stuff with myWindow
} );
I guess even attempting to set the size and position of the window after the callback isn't full-proof as is evident when I tried to do some under Windows 10.
The only remaining unknown to me is where is Google Chrome Apps storing all this window data when you give windows an id? And is there a way to clear that (other than uninstalling the app)?
I am using a hammer.js library with its jQuery plugin. I started using it as suggested by documentation, so something like this to initiate it on .game-card-mobile divs
/* Create Hammer object for swipable game cards */
var $gameCard = $('.game-card-mobile');
var $gameCardTouch = $gameCard.hammer();
$gameCardTouch.on("panright press panleft", function(ev) {
console.log(ev.type);
});
This allows me to restrict available actions to pan/swipe element to the right, left and press on it, however during one event, lets say panright I would get many entries printed out in console although only one pan was performed. So I then changed initiation to this:
$gameCardTouch.on("panend", function(ev) {
console.log(ev.type);
});
Now it listens to panend action which occur at the end of the action, thus is only returning one printout in console, however it is always panend now, thus I lost restriction to only 3 previous actions and am unable to detect what specific action was performed.
Is there a way to combine these so I would get a print out of panright if users swiped right, panleft if swiped left and press, all just one time once they finish that action?
Just using a combination of the panright,press, and panleft with the panend could work for you.
(function(){
var direction;
var $gameCard = $('.game-card-mobile');
var $gameCardTouch = $gameCard.hammer();
$gameCardTouch.on("panright press panleft", function(ev) {
//Set the direction in here
direction = ev.type;
});
$gameCardTouch.on("panend", function(ev) {
//Use the direction in here
//You know the pan has ended
//and you know which action they were taking
console.log(direction);
//So do what ever here
if(direction === "whatever") ...
});
}());
To expand a bit on what Jack was getting at. Perhaps a simpler solution...
ev.eventObject returns integer values
https://hammerjs.github.io/api/#event-object
In the case of ev.direction:
no movement = 0
left = 2
right = 4
up = 8
down = 16
horizontal = 6
vertical = 24
all = 30
Clean
var mc = new Hammer(body);
mc.on("panend", function(ev) {
if (ev.direction == INT) {
//do something
}
});
Verbosely
var mc = new Hammer(body);
mc.on("panend", function(ev) {
//console.log(ev.direction);
//for left case
if (ev.direction == 2) {
//do something
}
//for right case
if (ev.direction == 4) {
//do something
}
});
I'm on my phone, so this is just pseudo code, but can't you do something like this:
var eventHandlers = {
panright: function(){},
press: function(){},
panleft:function(){}
}
$gameCardTouch.on("panright press panleft", function(ev) {
if(ev.type === 'panright') {
eventHandlers.panright();
} else if((ev.type === 'panleft'){
eventHandlers.panright();
} else {
eventHandlers.press();
}
This example from the website is similar: http://codepen.io/jtangelder/pen/ABFnd
Was working on a similar problem using Backbone, jQuery, and Hammer. Here's how I solved it (only posting relevant bits). Hopefully someone finds this useful.
var Slideshow = Backbone.View.extend( {
events: {
"pan .slideshow-slide" : "handlePan",
"panstart .slideshow-slide" : "handlePanStart",
"panend .slideshow-slide" : "handlePanEnd",
},
state: {
panHistory: [],
},
handlePanStart: function( evt ) {
// attempt to get the pan x coord
var startX = this.getPanXFromEvent( evt );
// if an x coord couldn't be retrieved, get out
if ( !startX ) {
this.logger.warn( "Pan Start: Unable to find x", evt );
return;
}
this.logger.log( "Pan Start", evt );
// set the pan x array so this is its first and only element
this.state.panHistory = [ startX ];
},
handlePan: function( evt ) {
// cache the pan history
var pans = this.state.panHistory,
// get the x coord from this pan event
lastX = this.getPanXFromEvent( evt );
// track deltas on the x axis during the pan, so we know if the user
// is switching directions between pan start and pan end
if ( lastX ) {
pans.push( lastX );
}
},
handlePanEnd: function( evt ) {
// get the direction of the pan
switch ( this.getDirectionFromPanEvent( evt ) ) {
case Hammer.DIRECTION_LEFT:
// if we panned left and the next slide isn't out of
// range, go to the next slide.. otherwise fall back
// on the switch statement's default
if ( !this.isOutOfRange( this.state.current + 1 ) ) {
this.logger.log( "Pan End: Moving Left", evt );
this.gotoNextSlide();
return;
}
case Hammer.DIRECTION_RIGHT:
// if we panned right and the previous slide isn't out
// of range, go to the previous slide.. otherwise fall
// back on the switch statement's default
if ( !this.isOutOfRange( this.state.current - 1 ) ) {
this.logger.log( "Pan End: Moving Right", evt );
this.gotoPrevSlide();
return;
}
// Snap back to the current slide by default by calling
// gotoSlide on the current index to reset the transform
default:
this.logger.log( "Pan End: Snapping Back", evt );
this.gotoSlide( this.state.current );
}
},
getPanXFromEvent: function( evt ) {
return Utils.getValue( "originalEvent.gesture.center.x", evt );
},
getDirectionFromPanHistory: function( evt ) {
// placeholders for start, end, and last pan x coords
var i, start, last, end,
// cache the pan x array so we don't have to type it 50 times
pans = this.state.panHistory;
// if there aren't enough pans to calculate a delta, return 0
if ( pans.length < 2 ) {
return 0;
}
// get the starting pan x
start = pans[ 0 ];
// set last and end to the last pan x
last = end = pans[ pans.length - 1 ];
// loop backwards through the pans to find a pan x coord different from the ending one
// since there's a chance that identical pan x coords will be stacked in the array
for ( i = pans.length - 2; last === end && i >= 0; i-- ) {
last = pans[ i ];
}
// if the last pan was to the right, and we're farther right
// than we started, move right
return end > last && end > start ? Hammer.DIRECTION_RIGHT
// if the last pan was to the left, and we're farther left
// than we started, move left
: end < last && end < start ? Hammer.DIRECTION_LEFT
// otherwise move nowhere
: 0;
},
} );
I have a problem want to share with you.
So here it is. Imagine I have a very large image and it take a heavy loading when I open my page. Is it possible if I want to have the effect like cut the image to many smaller pieces and merge them one by one while the image is loading(just javascript, jquery, css and html code).
html:
<div>
<div style="float:left; width: 200px; height: 200px;">
<img id="imgHeavy" src="http://i.stack.imgur.com/kLQe4.png" width="200"/>
</div>
<div id="frameMerge" style="float:left; width: 200px; height:200px; background: #ddd;">
</div>
</div>
So now I want to set the background of my element(#frameMerge) is every single pieces of the image(#imgHeavy) that have been cut when the page is opened.
Please take a look at 2 pictures!
My img:
My div element:
Any idea would be appreciated!
You can use the functionality of multiple background images provided by css3, but it won't be faster in the end as the data being loaded with multiple pictures is even bigger (header-information of every single file) than with just one. The only difference would be, that you see parts of the image before the rest is loaded. But you can have the same effect if your image is a jpg. So you can use a progressive jpg that will show parts of your image while the rest is still loading.
You can do it with CSS3 and jquery.
Here is an example for you :
jsfiddle.net/elclanrs/4nsJE/
;(function( $, window ) {
var _defaults = {
x : 2, // number of tiles in x axis
y : 2, // number of tiles in y axis
random : true, // animate tiles in random order
speed : 2000 // time to clear all times
};
/**
* range Get an array of numbers within a range
* #param min {number} Lowest number in array
* #param max {number} Highest number in array
* #param rand {bool} Shuffle array
* #return {array}
*/
function range( min, max, rand ) {
var arr = ( new Array( ++max - min ) )
.join('.').split('.')
.map(function( v,i ){ return min + i })
return rand
? arr.map(function( v ) { return [ Math.random(), v ] })
.sort().map(function( v ) { return v[ 1 ] })
: arr
}
// Prevent css3 transitions on load
$('body').addClass('css3-preload')
$( window ).load(function(){ $('body').removeClass('css3-preload') })
$.fn.sliced = function( options ) {
var o = $.extend( {}, _defaults, options );
return this.each(function() {
var $container = $(this);
/*---------------------------------
* Make the tiles:
---------------------------------*/
var width = $container.width(),
height = $container.height(),
$img = $container.find('img'),
n_tiles = o.x * o.y,
tiles = [], $tiles;
for ( var i = 0; i < n_tiles; i++ ) {
tiles.push('<div class="tile"/>');
}
$tiles = $( tiles.join('') );
// Hide original image and insert tiles in DOM
$img.hide().after( $tiles );
// Set background
$tiles.css({
width: width / o.x,
height: height / o.y,
backgroundImage: 'url('+ $img.attr('src') +')'
});
// Adjust position
$tiles.each(function() {
var pos = $(this).position();
$(this).css( 'backgroundPosition', -pos.left +'px '+ -pos.top +'px' );
});
/*---------------------------------
* Animate the tiles:
---------------------------------*/
var tilesArr = range( 0, n_tiles, o.random ),
tileSpeed = o.speed / n_tiles; // time to clear a single tile
// Public method
$container.on( 'animate', function() {
tilesArr.forEach(function( tile, i ) {
setTimeout(function(){
$tiles.eq( tile ).toggleClass( 'tile-animated' );
}, i * tileSpeed );
});
});
});
};
}( jQuery, window ));
$('.sliced').sliced({ x: 6, y: 4, speed: 1000 });
$('button').click(function() {
$('.sliced').trigger('animate');
});
Of course you can just put an onload event on every segment (must be on <img> though) that makes it fade in. However, actually splitting the image into segments cannot be done on client side. You will need to either manually split the image beforehand or rely on server side scripting (e.g. php) to do this.
Also, do note that doing this will create quite a lot of overhead depending on the amount of segments you use, since every segment will need to make a new request to the server including downloading the file headers for every image.
Ok so i'm learning to write plugins and the jQuery site for plugin authoring is damn confusing. I'm really trying to figure out how to add methods to my plugin that will fire based on a browser event. So basically I have a plugin that builds out a swipeable slide show for a mobile device. When the device is rotated I need it to rebuild the slideshow for the new dimensions. Right now I have it so it builds it the first time but I can't (a) figure out to add methods for different functions and (b) bind events to those specific methods. here's my code.
(function( $ ){
$.fn.jCarousel = function( options ) {
var empty = {}
var defaults = {
height: $(window).height(),
width: $(window).width(),
count: 1,
amount: $('.swiper', this).length,
thisone:this,
};
var options = $.extend(empty, defaults, options);
return this.each(function() {
options.thisone = this;
amount = $('.swiper', this).length;
holdWidth = options.width * options.amount;
$('.swiper', this).height(options.height);
$(this).height(options.height);
$('.swiper', this).width(options.width);
$(this).width(holdWidth);
$('.swipe_slide',this).width(holdWidth);
//==================================================================================================================
//==================================================================================================================
$('.swiper', this).each(function(i) {
var nwidth = options.width*i;
$(this).css('left',nwidth);
});
//==================================================================================================================
//==================================================================================================================
//==================================================================================================================
//==================================================================================================================
$('.swipe_slide', this).swipe(function(evt, data) {
if(data.direction=='left'){
//alert('count: '+options.count+" amount: "+options.amount);
if(options.count != options.amount){
moveWidth = options.count * -options.width;
$('.swipe_slide',options.thisone).css( "left" ,moveWidth );
options.count++
}else{
return
}
}else{
if(options.count!=1){
moveWidth = moveWidth+ options.width;
$('.swipe_slide',options.thisone).css( "left" ,moveWidth );
options.count--
}
else{
return
}
}
});
//==================================================================================================================
//==================================================================================================================
});
};
})( jQuery );
$(window).bind('orientationchange', $.fn.jCarousel);
You'll have to refactor your plugin a bit so that the function stores the options that are passed into it, otherwise running the function again on orientation change will reset the options to the defaults.