bounty

Crazy SVG odometer effect library

 

Demo

Usage

bounty.default({ el: '.js-bounty', value: '£42,000,000' });

Usage

bounty.default({
  el: '.js-bounty',
  value: '£42,000,000',
  lineHeight: 1.35,
  letterSpacing: 1,
  animationDelay: 100,
  letterAnimationDelay: 100
});

Customized by CSS

.js-bounty {
  font-size: 60px;
  font-family: Roboto;
  fill: #fff;
  text-shadow: 1px 1px 5px rgba(0, 0, 0, 0.5);
}

Customized by CSS

Why SVG?

Why SVG?

  1. I ❤️ using SVG scalable beautiful vectors

  2. SVG Filters can be applied to HTML nodes as well*

  3. Masks clipping, gradients

  4. Easily customizable fill, stroke

  5. Drawings shapes, lines

  6. Great control over views and elements

* Still lack of support custom filters in many browsers

How it's made

Clip Region

More Rotation

Motion Blur

Gradient Mask

Implementation

  1. Selection Module DOM manipulation

  2. Transition Module

  3. Core

Selection Module

Strongly inspired by D3.js syntax and helpers

d3.select("body")
  .selectAll("p")
  .data([4, 8, 15, 16, 23, 42])
  .enter().append("p")
    .text(function(d) { return "I’m number " + d + "!"; });

Selection Module

Function Bind Syntax

obj::func
// is equivalent to:
func.bind(obj)

obj::func(val)
// is equivalent to:
func.call(obj, val)

::obj.func(val)
// is equivalent to:
func.call(obj, val)

Selection Module

Function Bind Syntax

// Function Bind Syntax
::console.log

//is equivalent to:
console.log.bind(console)

Method extraction

Selection Module

Function Bind Syntax

Virtual method

const { 
  map,
  forEach
} = Array.prototype;

// Function Bind Syntax
getPlayers()
  ::map(x => x.stop())
  ::forEach(x => log(x));
const { 
  map,
  forEach
} = Array.prototype;

// ES2015
let _ = getPlayers();
_ = map.call(_, x => x.stop());
_ = forEach.call(_, x => log(x));

Selection Module

Implementation

export default function (value) {
  this.textContent = value;
  return this;
}

text

Selection Module

Implementation

export default function (name, value) {
  this.setAttribute(name, value);
  return this;
}

attr

Selection Module

Implementation

export default function (name, value, priority = '') {
  this.style.setProperty(name, value, priority);
  return this;
}

style

Selection Module

Results

const setViewBox = (svg, width, height) => svg
  ::attr('width', width)
  ::attr('height', height)
  ::attr('viewBox', `0 0 ${width} ${height}`)
  ::style('overflow', 'hidden');

selection usage

Filters

Implementation

const motionBlur = (defs, id) => defs::append('filter')
  ::attr('id', `motionFilter-${id}`)
  ::attr('width', '300%')
  ::attr('x', '-100%')
  ::append('feGaussianBlur')
    ::attr('class', 'blurValues')
    ::attr('in', 'SourceGraphic')
    ::attr('stdDeviation', '0 0');

Motion Blur

Filters

Results

<filter id="motionFilter-1-1475053429366" 
        width="300%" 
        x="-100%">
  <feGaussianBlur class="blurValues" 
                  in="SourceGraphic" 
                  stdDeviation="0 0">
  </feGaussianBlur>
</filter>

Motion Blur Markup

Issues

Font Loading API only present in Chrome and FF

CSS Text Shadow on SVG nodes safari, IE

Filters clipping area all

Optimizations

chars.forEach(char => {
  // Read
  const { width } = char.node.getBBox();
  char.offset.x = canvasWidth;
  canvasWidth += width + letterSpacing;
  // Write
  char.node::attr('transform', `translate(${char.offset.x}, ${char.offset.y})`);
});

Before

Optimizations

chars.forEach(char => {
  // Read
  const { width } = char.node.getBBox();
  char.offset.x = canvasWidth;
  canvasWidth += width + letterSpacing;
});

chars.forEach(char => {
  // Write
  char.node::attr('transform', `translate(${char.offset.x}, ${char.offset.y})`);
});

After

Thank You

Michał Skowronek

@coderitual

 

Contribution

https://github.com/coderitual/bounty

 

bounty

By coderitual

bounty

  • 13,895