Code Monkey home page Code Monkey logo

ember-in-viewport's Introduction

ember-in-viewport

Detect if an Ember View or Component is in the viewport @ 60FPS

ember-in-viewport is built and maintained by DockYard, contact us for expert Ember.js consulting.

Read the blogpost

Download count all time npm version GitHub Actions Build Status Ember Observer Score

This Ember addon adds a simple, highly performant Service or modifier to your app. This library will allow you to check if a Component or DOM element has entered the browser's viewport. By default, this uses the IntersectionObserver API if it detects it the DOM element is in your user's browser – failing which, it falls back to using requestAnimationFrame, then if not available, the Ember run loop and event listeners.

We utilize pooling techniques to reuse Intersection Observers and rAF observers in order to make your app as performant as possible and do as little works as possible.

Demo or examples

Table of Contents

Installation

ember install ember-in-viewport

Usage

Usage is simple. First, inject the service to your component and start "watching" DOM elements.

import Component from '@glimmer/component';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';

export default class MyClass extends Component {
  @service inViewport

  @action
  setupInViewport() {
    const loader = document.getElementById('loader');
    const viewportTolerance = { bottom: 200 };
    const { onEnter, _onExit } = this.inViewport.watchElement(loader, { viewportTolerance });
    // pass the bound method to `onEnter` or `onExit`
    onEnter(this.didEnterViewport.bind(this));
  }

  didEnterViewport() {
    // do some other stuff
    this.infinityLoad();
  }

  willDestroy() {
    // need to manage cache yourself
    const loader = document.getElementById('loader');
    this.inViewport.stopWatching(loader);

    super.willDestroy(...arguments);
  }
}
<ul>
  <li></li>
  ...
</ul>
<div id="loader"></div>

You can also use Modifiers as well. Using modifiers cleans up the boilerplate needed and is shown in a later example.

Configuration

To use with the service based approach, simply pass in the options to watchElement as the second argument.

import Component from '@glimmer/component';
import { inject as service }  from '@ember/service';

export default class MyClass extends Component {
  @service inViewport

  @action
  setupInViewport() {
    const loader = document.getElementById('loader');

    const { onEnter, _onExit } = this.inViewport.watchElement(
      loader,
      {
        viewportTolerance: { bottom: 200 },
        intersectionThreshold: 0.25,
        scrollableArea: '#scrollable-area'
      }
    );
  }
}

Global options

You can set application wide defaults for ember-in-viewport in your app (they are still manually overridable inside of a Component). To set new defaults, just add a config object to config/environment.js, like so:

module.exports = function(environment) {
  var ENV = {
    // ...
    viewportConfig: {
      viewportUseRAF                  : true,
      viewportSpy                     : false,
      viewportListeners               : [],
      intersectionThreshold           : 0,
      scrollableArea                  : null,
      viewportTolerance: {
        top    : 0,
        left   : 0,
        bottom : 0,
        right  : 0
      }
    }
  };
};

// Note if you want to disable right and left in-viewport triggers, set these values to `Infinity`.

Modifiers

Using with Modifiers is easy.

You can either use our built in modifier {{in-viewport}} or a more verbose, but potentially more flexible generic modifier. Let's start with the former.

  1. Use {{in-viewport}} modifier on target element
  2. Ensure you have a callbacks in context for enter and/or exit
  3. options are optional - see Advanced usage (options)
<ul class="list">
  <li></li>
  <li></li>
  <div {{in-viewport onEnter=(fn this.onEnter artwork) onExit=this.onExit scrollableArea=".list"}}>
    List sentinel
  </div>
</ul>

This modifier is useful for a variety of scenarios where you need to watch a sentinel. With template only components, functionality like this is even more important! If you have logic that currently uses the did-insert modifier to start watching an element, try this one out!

If you need more than our built in modifier...

  1. Install @ember/render-modifiers
  2. Use the did-insert hook inside a component
  3. Wire up the component like so

Note - This is in lieu of a did-enter-viewport modifier, which we plan on adding in the future. Compared to the solution below, did-enter-viewport won't need a container (this) passed to it. But for now, to start using modifiers, this is the easy path.

import Component from '@glimmer/component';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';

export default class MyClass extends Component {
  @service inViewport

  @action
  setupInViewport() {
    const loader = document.getElementById('loader');
    const viewportTolerance = { bottom: 200 };
    const { onEnter, _onExit } = this.inViewport.watchElement(loader, { viewportTolerance });
    onEnter(this.didEnterViewport.bind(this));
  }

  didEnterViewport() {
    // do some other stuff
    this.infinityLoad();
  }

  willDestroy() {
    // need to manage cache yourself
    const loader = document.getElementById('loader');
    this.inViewport.stopWatching(loader);

    super.willDestroy(...arguments);
  }
}
<div {{did-insert this.setupInViewport}}>
  {{yield}}
</div>

Options as the second argument to inViewport.watchElement include:

  • intersectionThreshold: decimal or array

    Default: 0

    A single number or array of numbers between 0.0 and 1.0. A value of 0.0 means the target will be visible when the first pixel enters the viewport. A value of 1.0 means the entire target must be visible to fire the didEnterViewport hook. Similarily, [0, .25, .5, .75, 1] will fire didEnterViewport every 25% of the target that is visible. (https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Thresholds)

    Some notes:

    • If the target is offscreen, you will get a notification via didExitViewport that the target is initially offscreen. Similarily, this is possible to notify if onscreen when your site loads.
    • If intersectionThreshold is set to anything greater than 0, you will not see didExitViewport hook fired due to our use of the isIntersecting property. See last comment here: https://bugs.chromium.org/p/chromium/issues/detail?id=713819 for purpose of isIntersecting
    • To get around the above issue and have didExitViewport fire, set your intersectionThreshold to [0, 1.0]. When set to just 1.0, when the element is 99% visible and still has isIntersecting as true, when the element leaves the viewport, the element isn't applicable to the observer anymore, so the callback isn't called again.
    • If your intersectionThreshold is set to 0 you will get notified if the target didEnterViewport and didExitViewport at the appropriate time.
  • scrollableArea: string | HTMLElement

    Default: null

    A CSS selector for the scrollable area. e.g. ".my-list"

  • viewportSpy: boolean

    Default: false

    viewportSpy: true is often useful when you have "infinite lists" that need to keep loading more data. viewportSpy: false is often useful for one time loading of artwork, metrics, etc when the come into the viewport.

    If you support IE11 and detect and run logic onExit, then it is necessary to have this true to that the requestAnimationFrame watching your sentinel is not torn down.

    When true, the library will continually watch the Component and re-fire hooks whenever it enters or leaves the viewport. Because this is expensive, this behaviour is opt-in. When false, the intersection observer will only watch the Component until it enters the viewport once, and then it unbinds listeners. This reduces the load on the Ember run loop and your application.

    NOTE: If using IntersectionObserver (default), viewportSpy wont put too much of a tax on your application. However, for browsers (Safari < 12.1) that don't currently support IntersectionObserver, we fallback to rAF. Depending on your use case, the default of false may be acceptable.

  • viewportTolerance: object

    Default: { top: 0, left: 0, bottom: 0, right: 0 }

    This option determines how accurately the Component needs to be within the viewport for it to be considered as entered. Add bottom margin to preemptively trigger didEnterViewport.

    For IntersectionObserver, this property interpolates to rootMargin. For rAF, this property will use bottom tolerance and measure against the height of the container to determine when to trigger didEnterViewport.

    Also, if your sentinel (the watched element) is a zero-height element, ensure that the sentinel actually is able to enter the viewport.

Out of the box

Chrome 51 [1]
Firefox (Gecko) 55 [2]
MS Edge 15
Internet Explorer Not supported
Opera [1] 38
Safari Safari Technology Preview
Chrome for Android 59
Android Browser 56
Opera Mobile 37
  • [1] Reportedly available, it didn't trigger the events on initial load and lacks isIntersecting until later versions.
  • [2] This feature was implemented in Gecko 53.0 (Firefox 53.0 / Thunderbird 53.0 / SeaMonkey 2.50) behind the preference dom.IntersectionObserver.enabled.

Running

Running Tests

  • ember test
  • ember test --serve

Building

  • ember build

For more information on using ember-cli, visit http://www.ember-cli.com/.

Legal

DockYard, Inc © 2015

@dockyard

Licensed under the MIT license

Contributors

We're grateful to these wonderful contributors who've contributed to ember-in-viewport:

ember-in-viewport's People

Contributors

bcardarella avatar buschtoens avatar cibernox avatar csand avatar danorton-cubic-austin avatar dbashford avatar dependabot[bot] avatar duggiefresh avatar elidupuis avatar ember-tomster avatar homu avatar hybridmuse avatar jeffplang avatar jheth avatar jmurphyau avatar jpadilla avatar les2 avatar levelbossmike avatar lukemelia avatar mehulkar avatar michaeldupuis-dockyard avatar miguelcobain avatar offirgolan avatar poteto avatar rwjblue avatar sardyy avatar sergeastapov avatar snewcomer avatar vasind avatar xiphiasuvella 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

ember-in-viewport's Issues

RFC: Disable the mixin

The component that's using this mixin needs the ability to be dumb, ie. not watch the viewport at all, or not add any event listeners. Would the API be better to say if you set viewportRefreshRate to zero, it will not register any listeners, or would a new bool to disable the mixin be better?

Update to more recent ember-cli with ember-cli-babel@6

For new apps, ember-cli-babel@6 is used, which means that a complete babel@5 engine is installed along with this addon. This can be avoided by upgrading the dependency.

Note: ember-getowner-polyfill needs to be updated to v2.x as well, since it is required for the update.

Default options are missing

That one was hard to track down. I was getting

method.apply is not a function TypeError: method.apply is not a function
    at Object.Backburner.run (http://localhost:4000/assets/vendor.js:10755:27)
    at http://localhost:4000/assets/vendor.js:10997:28

I figured that it was due to not setting viewportRefreshRate in the config. The docs suggest that there are defaults and you don't have to set any config. It is true for viewportUseRAF, viewportEntered and viewportListeners but not for the other options: _setInitialState

I'm unsure where to set the defaults. I guess _setInitialState should be refactored to

  _setInitialState: on('init', function() {
    const options = this._buildOptions({
      viewportUseRAF: canUseRAF(),
      viewportEntered: false,
      viewportListeners: defaultListeners
      // all the other options
    });

    setProperties(this, options);
  })

If you are content with it I'll send a PR.

Attempting to register an unknown factory: `config:in-viewport`

Using ember-lazy-image I now get this error.
When I add viewportConfig to the environment.js it is gone.

To fix that we should add a fallback:

// app/initializers/viewport-config.js
application.register('config:in-viewport', viewportConfig || {}, { instantiate: false });

I tried const { viewportConfig } = config || {}; but that didn't worked so I did viewportConfig || {}

viewportEntered & viewportExited as "real" hooks

The README uses the term "hook" to refer to boolean values that are set on the root of the component. IMHO, these are "flags" (or something) but a hook would be a function that is invoked.

I would like to have functions that are called instead of forcing observation to consume the mixin. This would make it simpler to do complicated logic on entering/exiting the viewport without relying on using an observer.

We could use the following as a default implementation:

viewportEntered() {
  set(this, 'hasEnteredViewport', true);
  this.trigger('viewportEntered'); // send the `viewportEntered` event
}

Background: I am looking to swap ember-waypoints over to use ember-in-viewport instead of wrapping the jQuery plugin, and I would like to avoid having to use observation for triggering an action.

Mobile Safari minimal-ui mode

Issue

On mobile Safari didEnterViewport is firing late, due to incorrect calculation of height in minimal-ui mode. (On iPhone6 I had to scroll approximately 68px higher)

Solution

Proposed solution is using innerHeight instead of height() to calculate correct value on the spot. Here is a PR

Trigger when entering/exiting

Hi

Would it be possible to allow for triggering isEntering(), isExiting() hooks for when the element is 1px in or out of the viewport. I'm using this to do lazy image loading and I want to start loading the image as soon as 1px of it enters the viewport, however currently the hook doesn't fire until the while image is within the viewport.

Oli

Depreciation warnings

Not sure why I cannot pinpoint the issue, but I'm getting a bunch of depreciation warnings for the now retired didScrollUp (and other directions) even though I do not have these implemented in my code anyplace.

I get the same warnings on your demo app on divshot as well.

_unbindListeners() should clear the instance of the event listeners per element

This method clears all event listeners on the window and document (in reality, this mixin has only 3 listeners regardless of the number of Components, because of the way Ember registers event listeners globally), which means if you have >1 Component to watch, after one enters the viewport, it unbinds listeners for all other Components, whether or not they have entered the viewport.

tolerance for only the top part of an element

I can't seem to find a way to say, "trigger viewportEntered() as long as the top of the element is 50px in the viewport, and don't worry about the bottom."

My config:

  viewportTolerance: {
    top    : 50,
    bottom : 2000,  // I shouldn't have an element taller than this.
  }

Any thoughts anyone?

Support for rAF and scrolling up

<ul>
  {{loader-component}}
  <li>...</li>
  {{loader-component}}
</ul>

Currently, IntersectionObserver will fire didEnterViewport hook when in viewport if scrolled up. However, rAF currently only detects if in viewport IFF bottom < height. In the case of a loader component above a list of ul tags, this will not trigger correctly.

Given no IE support, it may be a good idea to support this type of feature.

Need to coearse bounding client rect values to integers

I originally reported this issue against ember-light-table here: adopted-ember-addons/ember-light-table#199

This comment has all the relevant details: adopted-ember-addons/ember-light-table#199 (comment)

For your convenience, I'm copying that text below:

TL;DR -- this is a floating point bug ... in ember-in-viewport addon.

This is really hard to reproduce because you need the numbers to round just right.

Details

The bug is in one of these two locations:

Example.

// values for bounding client rect of the `lt-infinity` element!
top:  318.85418701171875 left:  0 bottom:  318.85418701171875 right:  1451.1112060546875

// tolerance values:
topTolerance:  0
leftTolerance:  0
bottomTolerance:  500
rightTolerance:  0

// width and height of the window (viewport):
width:  1451 height:  820

// ALL of these have to be TRUE in order for the lt-infinity element to be *in* the viewport:
(top + topTolerance)       >= 0                   ? true
(left + leftTolerance)     >= 0                     ? true
(bottom - bottomTolerance) <= height    ? true
(right - rightTolerance)   <= width           ? FALSE     <---- this is the problem!

right boundary check in detail:

(right - rightTolerance)   <= width
1451.1112060546875 <= 1451    ===> FALSE

So we need to round (down) to integer value the bounding client rect values, I guess.

I'm going to look at a PR to the ember-in-viewport repo in a little bit and then open a PR to this repo that just bumps the version of that package.

Version

2.1.0

Test Case

Reproduced it with an ember-light-table based example described in this comment: adopted-ember-addons/ember-light-table#199 (comment)

I'm going to work on a PR to fix this in the next couple of days. I'm on paternity leave so please be patient with me!

Elements taller than viewport never trigger hooks

I'm running into an issue where tall components (taller than the viewport) don't report that they've entered or exited the viewport. Is there any way to trigger enter/exit/scroll hooks —regardless of whether the entire element is visible— other than by increasing viewportTolerance to some pixel value?

Using the waypoints library, one may use a percentage offset from top { offset: '100%' } and be notified as items enter the viewport, regardless of element size. Is this possible with ember-in-viewport?

Change in viewport without scroll don't trigger didEnterViewport

Version

2.1.1

Test Case

I have a list with images that are loaded when they are on the viewport.
When I scroll the images are loaded as expected.

Steps to reproduce

Expected Behavior

I have now a set of filters that filter the images in the list. When I change the filter I expect that the images that were hidden and now are in the viewport to be loaded.

Actual Behavior

The element that are now on the viewport never trigger didEnterViewport. If I scroll a bit they are immediately loaded.

Fastboot errors

document is not defined

Caused by

viewportListeners: [{ context: window, event: 'scroll.scrollable' }, { context: window, event: 'resize.resizable' }, { context: document, event: 'touchmove.scrollable' }],

Ember 2.9/2.10-beta compatibility? didEnterViewport no longer fires.

Big fan of the Mixin. I've found it extremely useful for a lot of my components.

I've recently upgraded my ember to 2.9, then subsequently to ember 2.10-beta for the glimmer engine.
However I noticed the Mixin is no longer working and the didEnterViewport no longer fires.

I've made no changes to the component and I've also upgraded the ember-in-viewport mixin to 2.1.0 so it should be the latest version, but to no avail.

Has anyone else come across this situation before?

Should this work with components in pod structure?

I have the following in /components/lineup-tile/component.js:

import Ember from 'ember';
import InViewportMixin from 'ember-in-viewport';

export default Ember.Component.extend(InViewportMixin, {
  le: null,
  didEnterViewport() {
    console.log('entered');
  },

  didExitViewport() {
    console.log('exited');
  },

  didScroll(direction) {
   console.log(direction); // 'up' || 'down' || 'left' || 'right'
 }
});

but all that happens is console.log('entered'); is fired 20 times when the app loads - there are 20 items but only 8 are immediately visible on screen - none of the other events ever fire?

rAF in Scrollable Area

Looking at using this addon in Safari, if in a scrollable container, getBoundingClientRect is computed based off of the window. So if the table is set 200px below or at the top of the screen, getBoundingClientRect on the element (sentinel) may not be sufficient.

If using scrollable container, we need to also get the client rect for the scrollable container and determine if in viewport IFF bottom (sentinel) - bottomTolerance (scrollable) <= height (scrollable) + top (scrollable).

Only called first time

Hello, I have a paginate-loader component that is using this mixin for infinite scroll, however it seems to only be called the first time it enters the viewport...

Here is my paginate-loader component...

import Ember from 'ember'
import InViewportMixin from 'ember-in-viewport'

PaginateLoaderComponent = Ember.Component.extend InViewportMixin,
  didEnterViewport: ->
    log 'entered'
    # Call the 'loadMore' action that is passed into the component.
    @sendAction 'loadMore'

export default PaginateLoaderComponent

in my templates/route.hbs

          {{#each stores as |store|}}}
          ...
          {{/each}}
          {{#paginate-loader loadMore=(route-action 'loadMore')}}
          loading...
          {{/paginate-loader}}

So when I scroll down and the paginate-loader comes into view the first time, it loads more data and it goes out of view... however scrolling down further until it comes into view again does NOT trigger the didEnterViewport function again... Why is this? How can I have it called every time?

Falls at 2nd hurdle - (1) installed (2) doesn't get recognised

Seems like a great add-on, but in Ember 2.7 it cannot be found...?

component/trail-card.js

import Ember from 'ember';
import InViewportMixin from 'ember-in-viewport';

export default Ember.Component.extend(InViewportMixin, {
  didEnterViewport() {
    console.log('entered');
  },
});

Error in console:

228 Uncaught Error: Could not find module ember-in-viewport imported from trekclient/components/trail-card

Support for offsets?

We fairly recently switched away from ember-waypoints to ember-in-viewport. One of the nice things about the former was the ability to enter offsets. We have headers in our app, so it would be great if there was a straightforward way to redefine the context and use a an html element rather than the window, or at the very least, give a pixel value offset. So if our header was 50px tall, we could set the offset 50px from the top to achieve our desired functionality.

Is there any way to accomplish this using the addon as it stands today? If I'm understanding tolerance correctly, it's not exactly what I'm looking for.

Cheers!

Use #ember-testing-container as a context when testing

Hi,

when updating to Ember 2.17 our tests start failing due to invalid didEnterViewport events - component with InViewport mixin was in window viewport, but was outside the #ember-testing-container viewport.

It seems that InViewport mixin should use #ember-testing-container when testing as a context instead of window here

isInViewport detection should be also changed accordingly

Happy to work on this if this makes sense.

Thanks

Consider refactor using IntersectionObserver API

rAF was a good stop gap over the more costly polling solution to enable smoother scrolling events, but even it has its limits perf wise. With ItnersectionObserver heading towards acceptance it could be a better long-term solution and ember-in-viewport should consider this the preferred method.

I would advocate for detecting the IntersectionObserver API, if it is available use it. If not fall back to the existing method of rAF, then more legacy Web APIs

Notes: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
Example: https://codepen.io/SimonEvans/pen/Qqjxde

Setting viewportOverrides with Ember.on doesn't work as expected

In the readme, it is recommended to override options like this:

export default Ember.Component.extend(InViewportMixin, {
  viewportOptionsOverride: Ember.on('didInsertElement', function() {
    Ember.setProperties(this, {
      viewportUseRAF            : true,
      viewportSpy               : false,
      viewportScrollSensitivity : 1,
      viewportRefreshRate       : 150,
      viewportTolerance: {
        top    : 50,
        bottom : 50,
        left   : 20,
        right  : 20
      }
    });
  })
});

However, I noticed that setting viewportSpy wasn't working for me. AFAIK, when Ember.on runs is actually non-deterministic. Since we're setting up the observer in the didInsertElement hook in the addon, I actually think it's more appropriate to override like this:

export default Ember.Component.extend(InViewportMixin, {
  didInsertElement() {
    Ember.setProperties(this, {
      viewportUseRAF            : true,
      viewportSpy               : false,
      viewportScrollSensitivity : 1,
      viewportRefreshRate       : 150,
      viewportTolerance: {
        top    : 50,
        bottom : 50,
        left   : 20,
        right  : 20
      }

      this._super(...arguments);
    });
  })
});

to ensure that the properties are set before the InViewportMixin runs.

Does that sound correct? I'll happily submit a PR updating the docs if this sounds reasonable.

Only detecting when elements are above or below viewport

Is there some way to configure this to only detect when the element is above or below the viewport? I don't care if it went off the page sideways and only want to check if it scrolled off the top.

I was able to achieve the effect by setting viewportTolerance.left and viewportTolerance.right to a really large, arbitrary number, but a way to disable the left-right check altogether would be even better since I figure there are some computations that could then be avoided.

Error in _unbindListeners in component integration tests

After mixing this into a component, integration tests keep throwing Uncaught TypeError: Cannot read property 'forEach' of undefined. This is on Ember 2.2.

It's failing when it's finding that viewportListeners hasn't been set.

Is there something I need to do to my test to get this to work like it should? Otherwise, what's the best way to test a component with this mixed in?

Bottom and Top Margin Issues with IntersectionObserver

Issue

RAF

Currently we use bottom to determine when we are in-viewport here. This wasn't documented but ppl used it to start fetching before reaching the sentinel element.

IntersectionObserver

bottom actually extends the "capturing frame" of your root. So in the case a user passed bottom: 500, in fact, top is actually what we need for IntersectionObserver. However, positive rootMargin does not seem to even add inset/outset margin anyways. I'll try to put together a codepen. Overflow:hidden / auto may be the culprit.

Investigate use of one Intersection Observer

#113 and @BartTK brought up one potential performance problem when using many intersection observers on a page (imaging thousands of components that are being observed by a separate intersection observer).

This could be easily solved with only one intersection observer per scrollable area to manage an array of entries. A simple change from unobserver to disconnect will also be needed.

Would be great to visit after #113 is merged.

Early bail out

@poteto is there a easy way to prevent the mixin from beeing initialized? Lets say i set viewportEntered = true and _setupElement would bail out immediately. That would be great for ember-lazy-image when the image is already in cache and would save a lot of performance.

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.