Skip to content

Commit

Permalink
Merge pull request #45 from ProjectSeptemberInc/animatedjs
Browse files Browse the repository at this point in the history
support Animated objects
  • Loading branch information
gre committed Jan 16, 2016
2 parents 6ad33aa + 5b01b8a commit db8a01b
Show file tree
Hide file tree
Showing 6 changed files with 366 additions and 83 deletions.
147 changes: 147 additions & 0 deletions src/AnimatedData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
const isAnimated = require("./isAnimated");

// At the moment, need to dup some things from RN Animated

class Animated {
__attach () {}
__detach () {}
__getValue () {}
__getAnimatedValue () { return this.__getValue(); }
__addChild () {}
__removeChild () {}
__getChildren () { return []; }
}

class AnimatedWithChildren extends Animated {
constructor () {
super();
this._children = [];
}
__addChild (child) {
if (this._children.length === 0) {
this.__attach();
}
this._children.push(child);
}
__removeChild (child) {
var index = this._children.indexOf(child);
if (index === -1) {
console.warn("Trying to remove a child that doesn't exist");
return;
}
this._children.splice(index, 1);
if (this._children.length === 0) {
this.__detach();
}
}
__getChildren () {
return this._children;
}
}

// Animated over the GL Data uniforms object
class AnimatedUniforms extends AnimatedWithChildren {
constructor (uniforms) {
super();
this._uniforms = uniforms;
this.__attach();
}

__getValue() {
const u = {};
const uniforms = this._uniforms;
for (let key in uniforms) {
let value = uniforms[key];
if (value instanceof Array) {
let arr = [];
for (let i = 0; i < value.length; i++) {
let v = value[i];
arr[i] = isAnimated(v) ? v.__getValue() : v;
}
u[key] = arr;
}
else if (isAnimated(value)) {
u[key] = value.__getValue();
}
else {
u[key] = value;
}
}
return u;
}

__attach() {
const uniforms = this._uniforms;
for (let key in uniforms) {
let value = uniforms[key];
if (value instanceof Array) {
for (let i = 0; i < value.length; i++) {
let v = value[i];
if (isAnimated(v)) {
v.__addChild(this);
}
}
}
else if (isAnimated(value)) {
value.__addChild(this);
}
}
}

__detach() {
const uniforms = this._uniforms;
for (let key in uniforms) {
let value = uniforms[key];
if (value instanceof Array) {
for (let i = 0; i < value.length; i++) {
let v = value[i];
if (isAnimated(v)) {
v.__removeChild(this);
}
}
}
else if (isAnimated(value)) {
value.__removeChild(this);
}
}
}
}

// Animated over a GL Data
class AnimatedData extends AnimatedWithChildren {
constructor (data, callback) {
super();
this._data = {
...data,
contextChildren: data.contextChildren.map(d => new AnimatedData(d)),
children: data.children.map(d => new AnimatedData(d)),
uniforms: new AnimatedUniforms(data.uniforms)
};
if (callback) this.update = callback;
this.__attach();
}

__getValue() {
const { ...data, contextChildren, children, uniforms } = this._data;
data.contextChildren = contextChildren.map(c => c.__getValue());
data.children = children.map(c => c.__getValue());
data.uniforms = uniforms.__getValue();
return data;
}

__attach() {
const { contextChildren, children, uniforms } = this._data;
contextChildren.forEach(c => c.__addChild(this));
children.forEach(c => c.__addChild(this));
uniforms.__addChild(this);
}

__detach() {
const { contextChildren, children, uniforms } = this._data;
contextChildren.forEach(c => c.__removeChild(this));
children.forEach(c => c.__removeChild(this));
uniforms.__removeChild(this);
}
}

module.exports = AnimatedData;
84 changes: 67 additions & 17 deletions src/createSurface.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const { fill, resolve, build } = require("./data");
const Shaders = require("./Shaders");
const findGLNodeInGLComponentChildren = require("./data/findGLNodeInGLComponentChildren");
const invariantStrictPositive = require("./data/invariantStrictPositive");
const AnimatedData = require("./AnimatedData");

let _glSurfaceId = 1;

Expand All @@ -27,37 +28,34 @@ module.exports = function (renderVcontainer, renderVcontent, renderVGL, getPixel
this._renderId = 0;
this._id = _glSurfaceId ++;
}

componentWillMount () {
Shaders._onSurfaceWillMount(this._id);
this._build(this.props);
this._attach();
}

componentWillUnmount () {
this._renderId = 0;
Shaders._onSurfaceWillUnmount(this._id);
this._dataAnimated && this._dataAnimated.__detach();
}
getGLCanvas () {
return this.refs.canvas;
}
captureFrame () {
const c = this.getGLCanvas();
invariant(c && c.captureFrame, "captureFrame() should be implemented by GLCanvas");
return c.captureFrame.apply(c, arguments);

componentWillReceiveProps (nextProps) {
this._build(nextProps);
this._attach();
}
render() {

_build (props) {
const id = this._id;
const renderId = ++this._renderId;
const props = this.props;
const {
style,
width,
height,
pixelRatio: pixelRatioProps,
children,
debug,
preload,
opaque,
visibleContent,
eventsThrough,
...restProps
preload
} = props;

const decorateOnShaderCompile = onShaderCompile =>
Expand Down Expand Up @@ -105,9 +103,61 @@ module.exports = function (renderVcontainer, renderVcontent, renderVGL, getPixel
Shaders._afterSurfaceBuild(id);
}

const { data, contentsVDOM, imagesToPreload } = resolved;
this._resolved = resolved;
this._pixelRatio = pixelRatio;

if (debug) logResult(resolved.data, resolved.contentsVDOM);
}

_attach () {
const oldDataAnimated = this._dataAnimated;
const callback = () => {
const canvas = this.getGLCanvas();
if (!canvas) return;
if (canvas.setNativeProps) {
const data = this._dataAnimated.__getValue();
canvas.setNativeProps({ data });
}
else {
this.forceUpdate();
}
};
this._dataAnimated = new AnimatedData(
this._resolved.data,
callback);

oldDataAnimated && oldDataAnimated.__detach();
}

if (debug) logResult(data, contentsVDOM);
getGLCanvas () {
return this.refs.canvas;
}

captureFrame () {
const c = this.getGLCanvas();
invariant(c, "c is '%s'. Is the component unmounted?", c);
invariant(c.captureFrame, "captureFrame() should be implemented by GLCanvas");
return c.captureFrame.apply(c, arguments);
}

render() {
const renderId = this._renderId;
const { contentsVDOM, imagesToPreload } = this._resolved;
const data = this._dataAnimated.__getValue();
const pixelRatio = this._pixelRatio;
const props = this.props;
const {
style,
width,
height,
children,
debug,
preload,
opaque,
visibleContent,
eventsThrough,
...restProps
} = props;

return renderVcontainer(
{ width, height, style, visibleContent, eventsThrough },
Expand Down
102 changes: 47 additions & 55 deletions src/data/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const invariant = require("invariant");
const Uniform = require("../Uniform");
const Shaders = require("../Shaders");
const TextureObjects = require("./TextureObjects");
const isNonSamplerUniformValue = require("./isNonSamplerUniformValue");
const duckTypeUniformValue = require("./duckTypeUniformValue");
const findGLNodeInGLComponentChildren = require("./findGLNodeInGLComponentChildren");
const unifyPropsWithContext = require("./unifyPropsWithContext");
const invariantStrictPositive = require("./invariantStrictPositive");
Expand Down Expand Up @@ -48,71 +48,63 @@ module.exports = function build (GLNode, context, parentPreload, via, surfaceId,
});

Object.keys(uniforms).forEach(name => {
let value = uniforms[name];
let nonSamplerUniformTyp = isNonSamplerUniformValue(value);
if (nonSamplerUniformTyp) {
if (process.env.NODE_ENV!=="production" && nonSamplerUniformTyp === "number[]") {
let i = value.length;
while (i-- > 0 && !isNaN(value[i]));
invariant(i < 0, "Shader '%s': uniform '%s' must be an array of numbers. Found '%s' at index %s", shaderName, name, value[i], i);
}
return;
}

let opts, typ = typeof value;

if (value && typ === "object" && !value.prototype && "value" in value) {
let value = uniforms[name], opts;
if (value && typeof value === "object" && !value.prototype && "value" in value) {
// if value has a value field, we tread this field as the value, but keep opts in memory if provided
if (typeof value.opts === "object") {
opts = value.opts;
}
value = value.value;
typ = typeof value;
}

if (!value) {
// falsy value are accepted to indicate blank texture
uniforms[name] = value;
}
else if (typ === "string") {
// uri specified as a string
uniforms[name] = TextureObjects.withOpts(TextureObjects.URI({ uri: value }), opts);
}
else if (typ === "object" && typeof value.uri === "string") {
// uri specified in an object, we keep all other fields for RN "local" image use-case
uniforms[name] = TextureObjects.withOpts(TextureObjects.URI(value), opts);
}
else if (typ === "object" && value.data && value.shape && value.stride) {
// ndarray kind of texture
uniforms[name] = TextureObjects.withOpts(TextureObjects.NDArray(value), opts);
}
else if(typ === "object" && (value instanceof Array ? React.isValidElement(value[0]) : React.isValidElement(value))) {
// value is a VDOM or array of VDOM
const res = findGLNodeInGLComponentChildren(value, newContext);
if (res) {
const { childGLNode, via } = res;
// We have found a GL.Node children, we integrate it in the tree and recursively do the same
try {
switch (duckTypeUniformValue(value)) {

children.push({
vdom: value,
uniform: name,
data: build(childGLNode, newContext, preload, via, surfaceId, decorateOnShaderCompile)
});
}
else {
// in other cases VDOM, we will use child as a content
contents.push({
vdom: value,
uniform: name,
opts
});
case "string": // uri specified as a string
uniforms[name] = TextureObjects.withOpts(TextureObjects.URI({ uri: value }), opts);
break;

case "{uri}": // uri specified in an object, we keep all other fields for RN "local" image use-case
uniforms[name] = TextureObjects.withOpts(TextureObjects.URI(value), opts);
break;

case "ndarray":
uniforms[name] = TextureObjects.withOpts(TextureObjects.NDArray(value), opts);
break;

case "vdom[]":
case "vdom":
const res = findGLNodeInGLComponentChildren(value, newContext);
if (res) {
const { childGLNode, via } = res;
// We have found a GL.Node children, we integrate it in the tree and recursively do the same
children.push({
vdom: value,
uniform: name,
data: build(childGLNode, newContext, preload, via, surfaceId, decorateOnShaderCompile)
});
}
else {
// in other cases VDOM, we will use child as a content
contents.push({
vdom: value,
uniform: name,
opts
});
}
break;

default:
// Remaining cases will just set the value without further transformation
uniforms[name] = value;
}
}
else {
// in any other case, it is an unrecognized invalid format
catch (e) {
delete uniforms[name];
if (typeof console !== "undefined" && console.error) console.error("invalid uniform '"+name+"' value:", value); // eslint-disable-line no-console
invariant(false, "Shader '%s': Unrecognized format for uniform '%s'", shaderName, name);
const message = "Shader '"+shaderName+"': uniform '"+name+"' "+e.message;
if (process.env.NODE_ENV !== "production")
console.error(message, value); // eslint-disable-line no-console
throw new Error(message);
}
});

Expand Down
Loading

0 comments on commit db8a01b

Please sign in to comment.