Giter Club home page Giter Club logo

pig.js's Introduction

Progressive Image Grid (pig.js)

npm Bower

→ → Play with a demo

Arrange images in a responsive, progressive-loading grid managed in JavaScript using CSS transforms, with one lightweight library. Here's what you get:

  1. Performance: The pig.js grid specializes in displaying a large number of photos. While a more traditional approaches would paginate images, or use AJAX to load and insert buckets of additional images onto the page, pig.js intelligently loads and unloads images as you scroll down the page, to ensure ensure speedy rendering of images, smooth scrolling, and minimal network activitiy.
  2. Responsiveness: Images are loaded in JavaScript, at different resolutions depending on screen size. Also, because images are positioned using CSS transforms, images smartly re-tile when you shrink or expand the browser window.
  3. User Experience: Images are previewed with a small placeholder image that is scaled and blured. When the image loads, it gives the effect that the image was brought into focus (it's super cool!).

A lot of this is stolen straight from Google Photos (by way of observation and some creative use of the Chrome Inspector) and Medium (by way of some dope blog posts).

If you want to see pig.js in action, check out the site that motivated it in the first place: a catalog of everything I ate in 2015: feeding.schlosser.io. That site is also on GitHub.

Getting Started

Step 0: Install

Download the latest release.

Step 1: Create your markup

<!-- Include the pig.js library -->
<script src="/path/to/pig.min.js"></script>

<!-- Create a container element for the grid. -->
<div id="pig"></div>

Step 2: Create a structure to serve your images

Pig includes by default an easy method for handling responsive images at different screen sizes. By default, Pig will attempt to request images with size (height in pixels) 100, 250, and 500. It will also request a thumbnail 20px tall, which is used to create an effect of the blurred image coming into focus.

Example: Serving Static Files

Create a directory structure like:

.
├── index.html
├── path
│   └── to
│       └── pig.min.js
├── img
│   ├── 20
│   |   ├── blue.jpg
│   |   ├── red.jpg
│   |   ...
│   |
│   ├── 100
│   |   ├── blue.jpg
│   |   ├── red.jpg
│   |   ...
│   |
│   ├── 250
│   |   ├── blue.jpg
│   |   ├── red.jpg
│   |   ...
│   |
│   └── 500
│       ├── blue.jpg
│       ├── red.jpg
│       ...
...

And then set the urlForSize configuration option to account for this structure:

var options = {
  urlForSize: function(filename, size) {
    return '/img/' + size + '/' + filename;
  },
  // ...
};

Step 3: Create a new Pig instance, passing image data and options

var imageData = [
  {filename: 'blue.jpg', aspectRatio: 1.777},
  {filename: 'red.jpg', aspectRatio: 1.5},
  {filename: 'green.jpg', aspectRatio: 1.777},
  {filename: 'orange.jpg', aspectRatio: 1.777},
  {filename: 'yellow.jpg', aspectRatio: 1},
  {filename: 'purple.jpg', aspectRatio: 2.4},
];

var options = {
  urlForSize: function(filename, size) {
    return '/img/' + size + '/' + filename;
  },
  // ...
};

var pig = new Pig(imageData, options).enable();

API

Pig(imageData[, options])

The Pig constructor will setup a new progressive image grid instance. It returns a new instance of the Pangea class.

imageData (array)

Note: This argument is required.

A list of objects, one per image in the grid. In each object, the filename key gives the name of the image file and the aspectRatio key gives the aspect ratio of the image. The below example shows how you would pass six images into the PIG:

var imageData = [
  {filename: 'blue.jpg', aspectRatio: 1.777},
  {filename: 'red.jpg', aspectRatio: 1.5},
  {filename: 'green.jpg', aspectRatio: 1.777},
  {filename: 'orange.jpg', aspectRatio: 1.777},
  {filename: 'yellow.jpg', aspectRatio: 1},
  {filename: 'purple.jpg', aspectRatio: 2.4},
];
var options = { /* ... */ };
var pig = new Pig(imageData, options);

options (object)

You can customize the instance by passing the options parameter. The example below uses all options and their defaults:

var imageData = [ /* ... */ ];
var options = {
  containerId: 'pig',
  classPrefix: 'pig',
  figureTagName: 'figure',
  spaceBetweenImages: 8,
  transitionSpeed: 500,
  primaryImageBufferHeight: 1000,
  secondaryImageBufferHeight: 300,
  thumbnailSize: 20,
  urlForSize: function(filename, size) {
    return '/img/' + size + '/' + filename;
  },
  onClickHandler: function(filename) { },
  getMinAspectRatio: function(lastWindowWidth) {
    if (lastWindowWidth <= 640)  // Phones
      return 2;
    else if (lastWindowWidth <= 1280)  // Tablets
      return 4;
    else if (lastWindowWidth <= 1920)  // Laptops
      return 5;
    return 6;  // Large desktops
  },
  getImageSize: function(lastWindowWidth) {
    if (lastWindowWidth <= 640)  // Phones
      return 100;
    else if (lastWindowWidth <= 1920) // Tablets and latops
      return 250;
    return 500;  // Large desktops
  }
};
var pig = new Pig(imageData, options);

options.containerId (string)

The class name of the element inside of which images should be loaded.

Default: 'pig'

options.classPrefix (string)

The prefix associated with this library that should be prepended to class names within the grid.

Default: 'pig'

options.figureTagName (string)

The tag name to use for each figure. The default setting is to use a <figure></figure> tag.

Default: 'figure'

options.spaceBetweenImages (number)

Size in pixels of the gap between images in the grid.

Default: 8

options.transitionSpeed (number)

Transition speed in milliseconds.

Default: 500

options.primaryImageBufferHeight (number)

Height in pixels of images to preload in the direction that the user is scrolling. For example, in the default case, if the user is scrolling down, 1000px worth of images will be loaded below the viewport.

Default: 1000

options.secondaryImageBufferHeight (number)

Height in pixels of images to preload in the direction that the user is NOT scrolling. For example, in the default case, if the user is scrolling down, 300px worth of images will be loaded above the viewport. Images further up will be removed.

Default: 300

options.thumbnailSize (number)

The height in pixels of the thumbnail that should be loaded and blurred to give the effect that images are loading out of focus and then coming into focus.

Default: 20

options.urlForSize (function)

Get the URL for an image with the given filename & size.

Parameters:

  • filename (string) - The filename of the image.
  • size (number) - The size (height in pixels) of the image.

Returns:

  • (string) - The URL of the image at the given size.

Default:

function(filename, size) {
  return '/img/' + size + '/' + filename;
}

options.getMinAspectRatio (function)

Get the minimum required aspect ratio for a valid row of images. The perfect rows are maintained by building up a row of images by adding together their aspect ratios (the aspect ratio when they are placed next to each other) until that aspect ratio exceeds the value returned by this function. Responsive reordering is achieved through changes to what this function returns at different values of the passed parameter lastWindowWidth.

Parameters:

  • lastWindowWidth (number) - The last computed width of the browser window.

Returns:

  • (number) - The minimum aspect ratio at this window width.

Default:

function(lastWindowWidth) {
  if (lastWindowWidth <= 640)  // Phones
    return 2;
  else if (lastWindowWidth <= 1280)  // Tablets
    return 4;
  else if (lastWindowWidth <= 1920)  // Laptops
    return 5;
  return 6;  // Large desktops
}

options.getImageSize (function)

Get the image size (height in pixels) to use for this window width. Responsive resizing of images is achieved through changes to what this function returns at different values of the passed parameter lastWindowWidth.

Parameters:

  • lastWindowWidth (number) - The last computed width of the browser window.

Returns:

  • (number) - The size (height in pixels) of the images to load.

Default:

function(lastWindowWidth) {
  if (lastWindowWidth <= 640)  // Phones
    return 100;
  else if (lastWindowWidth <= 1920) // Tablets and latops
    return 250;
  return 500;  // Large desktops
}

options.onClickHandler (function)

Add callback function which is called when a image is clicked with the image name. By default this is an empty function.

Parameters

  • filename (string) - The name of the clicked image

Default function(filename) {}

Pig.enable()

Enable the Pig library by beginning to listen to scroll and resize events, loading images and displaying them in the grid.

Pig.disable()

Disable the Pig library by removing event listeners set in Pig.enable().

pig.js's People

Contributors

bepo65 avatar fliiiix avatar kaidero avatar mvitus avatar ortham avatar schlosser avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pig.js's Issues

Page scroll reset when setting pig div to "display: none"

In the onClickHandler, say I have

var pigOptions = {
  urlForSize: function(imageData, size) {
    //...
  },
  onClickHandler: function(filename) {
    document.getElementById("pig").classList.add("hide");
    // code to show bigger pic of filename here

  },
};

corresponding css

.hide{
  display: none;
}

... in order to hide the contents of the pig div and show the photo in context, I'm seeing that the scroll event is called, and pig resets the output to the top.

Avoid unused click handlers for images

By now every image gets a click handler.
If the application doesn't need a click handler, an empty function is used anyway (the default value from the configuration).

If the default value in the configuration would be 'null', then we could add a click handler only, if one is specified in the configuration.

error using pig.js in an umd environment (angular 9)

First thanks for this great project - it solves most of the requirements for a superfast picture list in my current application.

I tried to use pig.js (current master branch - 1e98008) in an angular environment and therefore created a type definition file as angular is a typescript framework.
Whatever I tried, all I got was an error message, stating that typescript couldn't find a constructor.

In the end I had to change line 883 from define([], function() { return Pig; }); to define([], function() { return { Pig }; });.

So I am wondering if this project was ever used in an umd environment (because then I had to go back to my type definition)?
Otherwise (and if this project is still alive) I could create a PR from this if you appreciate that.

Can we Use custom layout instead of just images ?

This library is really awesome and helpful.
But I want to use customized layout where i could add captions, context menus and much more like that. Is there any way i could use it for that purpose?

Any suggestions would be really appreciated.
Thanks 😊

Possible bug with initial sizing of image layout

First of all, I wanted to say that you've done an amazing job with this library; I think it's fantastic. Of the libraries that I've looked into for displaying images in a tiled fashion, this is the one that has met my needs, especially since it displays them in rows (like in Google Photos), which I really like.

I've noticed what might be a bug, so I'll do my best to describe it. Suppose you have a page with a <div id="pig"> to hold the pig layout, as usual when using the library. I've noticed that when the page is first loaded, the image layout as a whole seems to be scaled a little too big, such that it oversteps the right-hand boundary of the div. One way to notice this is to look at a page that has left and right margins (or padding) of equal width on the sides of the content: when the page is first loaded, the right margin will appear a little narrower because the image layout is overstepping the boundary somewhat on the right side. However, if you resize the browser window after the initial page load, everything seems to get sized correctly (and, in this example, the margins would now appear equal).

I was able to observe this phenomenon in Chrome, Firefox, and Safari (all on desktop), both with a site I'm making, as well as with the https://feeding.schlosser.io/ site (which, by the way, is really cool—I love how you documented all that food!).

Are you able to observe this phenomenon as well? I'd love to help out with a fix if I'm able, but since I relatively inexperienced with JS (I've mostly done back-end work in Java thus far), and since I'm not yet very familiar with this library's implementation, I would probably need some pointers along the way.

In any case, thanks again for your great work!

Triggering script after pig.enable() has finished?

I'll preface this by saying I don't do much work in js, so apologies if this question is basic. And this isn't an issue directly with your library but I'm trying to figure out how I can call another js function that I've written AFTER the enable function finishes and all the pig html structure is in place.

I'm trying to run another script after the pig.js finishes it's stuff so that I can add a lightbox to my images. I tested the lightbox script in the chrome console and it works fine with pig.js but I can't seem to get the code to execute at the right time, I tried putting in a document ready function but the code was still getting called before pig.js finishes adding all the html structure. Resulting in my jQuery selectors returning empty.

Suggestions that you may or may not like but maybe you can include some kind of callback function that can be defined in the options array when calling pig.enable, so people can extend you script to work with other libraries such as lightboxes

Is there actual need for overflow: hidden in figure's css?

I noticed that the default styles add overflow: hidden; to the figure elements' styles.

I tried to understand why it is necessary, but to me it seems like it could be removed with no ill effects?

I even tried to see if anything broke after removing it, and all seems to be working just fine.

Do you have any edge cases or scenarios in mind that might benefit from that css rule?

Also, thanks for building this library!

bower

hi, do you mind if put your code to bower so people can use it?

urlForSize triggered too many times for api calls during fast scrolling

Hi,

First of all thanks for creating this fantastic library. Exactly what I need for my home photo gallery project.

Here is my use case: I load a couple hundred to a couple thousand photo thumbnails to pig.js and I would like to have a great scrolling/viewing experience. However, the images are hosted on a cloud storage provider and I can access them via api requests. Hence, I implemented the api calls to get the links to the images in the urlForSize. Works perfectly.

The problem is that when I scroll quickly to a different part of the image grid, the cloud api server is bombarded with hundreds of requests and it takes a significant amount of time to get the url of the actual images that are on the screen. The wait time can easily be 20-40 sec or more.

Do you have a solution for the above?

With some googling some ideas I could gather:

  1. Don't call heavy js code on scroll events as there is too many of them. Instead implement a timer and batch/filter the api calls on the timer event and not the scroll event.
  2. Find a way to only call the external api once scrolling is settled. I don't know how to do it.
  3. Drop the pending api requests that we no longer need (as we scrolled over them).

You may have a better idea though.

Thanks,
David

Externally hosted images

How would I use Pig with externally hosted images?

I am grabbing an array of posts from Facebook and the images are hosted there. Pig is automatically prepending my hostname to the front of the URL...and then ofc the image can't be found...

image

Integration with React

Hi there!

I have used your PIG library and it rocks big time. I have half-integrated it within a React project, and now it's time to refactor it so it doesn't use document.createElement anymore, calling React.createElement instead. I bet it's going to be fun.

If anyone is interested in participating or playing around with it, let me know. I am using this as an exercise to really learn React, so I am no expert. I hope doing this will change that 😄

Publish on npm

Thanks for making this great library! Any chance you might be able to publish it on npm?

video support

I've been contemplating using pig.js for a family media site, but one issue is I have multiple videos that I'd like to tile in besides just images.

I'm wondering how that could work. 🤔 Maybe it wouldn't be a drastic change?

Why are some images being rotated?

I've been working on integrating this library into a Drupal website so I dynamically pull images in and style them. A few issues I've come across:

  1. some images get rotated?
  2. the primary buffer seems a bit off? when scrolling you can see some images at the very top and bottom disappear before they are off the screen
  3. when you scroll images don't have a gray background like in the demo, it just looks like a blank page until the 20px thumbnail appears.
  4. the library is ignoring my 500px directory and only pulling pictures from the 250px directory, making some larger images upscale and look really bad. This also looks to be an issue on the demo. Unless I'm misunderstanding the documentation it looks like larger images should be using the 500px height images.

DEMO: http://photo.marknr.com/galleries

FIXES:

  1. snippet below taken from demo. I'd suggest updating the JS to add this inline styling. Having the demo have custom styling may confuse people such as myself as I was expecting my demo to look the same.
.pig-figure {
    background-color: #D5D5D5;
}

Automation tools & WordPress integration

I love this library! It's exactly what I was looking for. I wanted to use it in WordPress, and also automate as much as possible, so I created this. See it in action at https://modjeska.us/pictures-china-2014/.

I used Mark Rodgers's's version to take advantage of Swipebox functionality.

Everything works great, except ProgressiveImage.prototype.hide doesn't seem to play nice with WordPress. It caused jQuery errors and some images failed to load. I found that simply commenting it out solved the problem, and I didn't notice any real performance issues. Error returned was:

Uncaught NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is no longer a child of this node.

Cheers!

Offload Processing to Web Workers

Really enjoyed the Medium writeup- I have a gallery of food-related pictures that I've been thinking of turning into a project someday, so this might come in handy!

Wanted to ask if you'd looked into using WebWorkers to do some of the computation on background threads instead of the main Javascript thread? The catch with Web Workers is they can't touch the DOM directly, but you can pass mathematical / network request related tasks to them and they can give the result back to your main application thread. @kosamari gave an example in her OpenVis talk this year.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.