blackChef / jkf

A javascript animation library that use css-keyframes-animation syntax
3 stars 0 forks source link

Add timeline method #2

Open yckart opened 8 years ago

yckart commented 8 years ago

Would nice to be able to define all animations in one object. Where the parent selector is the context for each children. A second parameter could hold a selector-engine like jQuery.

Jkf.timeline(animations [, engine])

Jkf.timeline({
  '.section-1': {
    '.section-title': {
      0: { opacity: 1 },
      0.5: { opacity: 0 },
      1: { opacity: 1 }
    },
    '.section-body': {
      0: { scale: 0 },
      1: { scale: 1 }
    }
  },
  '.section-2': {
    '.section-title': {
      ...
    }
  }
}, window.jQuery)

It's maybe better to use arrays instead of plain objects, for performance reasons (and the case that objects are sorted differently by browsers).

Jkf.timeline([{
  element: '.section-1',
  animations: [{
    element: '.section-title',
    keyframes: {
      0: { opacity: 1 },
      0.5: { opacity: 0 },
      1: { opacity: 1 }
    }
  }, {
    element: '.section-body',
    keyframes: {
      ...
    }
  }]
}, {
  element: '.section-2',
  animations: [{
    element: '.section-title',
    keyframes: {
      ...
    }
  }]
}], window.jQuery)

...or maybe functional like the example below?

Jkf.timeline([
  Jkf.section('.section-1', [
    Jkf.animate('.section-title', {
      0: { opacity: 1 },
      0.5: { opacity: 0 },
      1: { opacity: 1 }
    }),
    Jkf.animate('.section-body', {
      ...
    })
  ]),
  Jkf.section('.section-2', [
    Jkf.animate('.section-title', {
      ...
    })
  ])
], window.jQuery)
blackChef commented 8 years ago

I don't think it's necessary. The main purpose of Jkf is to make it easier to create progress based interaction (touch / scroll). For example, to create a dragable side panel, you calculate progress depend on how long user's finger has moved. Then you use Jkf.update() update the panel's style. When user released his finger, pass the last progress as options.from to Jkf.animate(), let the panel animate the rest of it.

You can easily create a helper function with Jkf to animate many elements at once. But I want to keep api simple and low level.

yckart commented 8 years ago

The intention of this timeline was to animate scenes one after the other, not all at once.

As Jkf is also made for scroll interactions, I can see a great performance improvement. As the timeline-wrapper would contain all animations, it could make a pre-check to only animate the parts that are needed to be animated (eg. if only 2 of 5 sections are in the keyframe range).

A range-property could define the start / end points, where the child keyframe-points are relative to. If there's only one point in the range, this would be the duration of this section and the start-point would then the end-offset of the previous section.

Since this great library is already very neat for simple animations, a timeline-method would make it also ready for much more complex stuff, and could be probably solved in 30-50 lines of code.

var timeline = Jkf.timeline([{
  range: [0, 1337], // => [0, 1337]
  animations: [{
    keyframes: {
      0: {},    // => 0
      0.25: {}, // => 334.25
      0.5: {},  // => 668.5
      0.75: {}, // => 1002.75
      1: {}     // => 1337
    }
  }]
}, {
  range: [2000], // => [1337, 2000]
  animations: [{
    keyframes: {
      0: {},    // => 1337
      0.25: {}, // => 1502.75
      0.5: {},  // => 1668.5
      0.75: {}, // => 1834.25
      1: {}     // => 2000
    }
  }]
}, {
  range: [1000, 2000], // => [1000, 2000]
  animations: [{
    keyframes: {
      0: {},    // => 1000
      0.25: {}, // => 1250
      0.5: {},  // => 1500
      0.75: {}, // => 1750
      1: {}     // => 2000
    }
  }]
}])

The values in the comments are the internally calculated progress-offsets.

Taking the example above, timeline would only update the sections where the passed progress is in the range of a section.

// Only the 1st section is updated
timeline.update(0)

// Only the 1st section is updated
timeline.update(500)

// The 1st, 2nd and 3rd sections are updated
timeline.update(1000)

// The 2nd and 3rd sections are updated
timeline.update(1500)

This could probably be done without the range-property by using keyframe-points out of 0..1, but the approach above just feels more correct:

var timeline = Jkf.timeline([{
  animations: [{
    keyframes: {
      0: {},
      0.25: {},
      0.5: {},
      0.75: {},
      1: {}
    }
  }]
}, {
  animations: [{
    keyframes: {
      1: {},
      1.25: {},
      1.5: {},
      1.75: {},
      2: {}
    }
  }]
}])
blackChef commented 8 years ago

May be something like this:

// chaining for one element
Jkf.animate(elem, kf1, duration2, options1)
   .animate(kf2, duration2, options2)
   .animate(kf3, duration3, options3)

// promise like syntax for multiple elements
Jkf.animate(elem1, kf1, duration1, options1)
   .then(elem2, kf2, duration2, options2)
   .then(elem3, kf3, duration3, options3)
yckart commented 8 years ago

Great, just saw that you pushed something new, queuedAnimate. :+1: Could close this issue for now, but give me a few days to test some stuff...

Incidentally would'nt it nice if we could interact with this new helper. So, an update-method could really helpful, or at least returning the animate-calls.

Anyway, big thanks!

blackChef commented 8 years ago

What do you mean by returning update or animate? Leave it open, I'm not familar with all these github things, so whatever.