react-dollyzoom-effect
Version:
A React component for applying a dollyzoom effect to a layout.
609 lines (587 loc) • 22.3 kB
JavaScript
(function (global, factory) {
if (typeof define === "function" && define.amd) {
define(["exports", "react", "react-dom", "velocity-react", "velocity-animate", "lodash", "jquery"], factory);
} else if (typeof exports !== "undefined") {
factory(exports, require("react"), require("react-dom"), require("velocity-react"), require("velocity-animate"), require("lodash"), require("jquery"));
} else {
var mod = {
exports: {}
};
factory(mod.exports, global.react, global.reactDom, global.velocityReact, global.velocityAnimate, global.lodash, global.jquery);
global.index_ = mod.exports;
}
})(this, function (exports, _react, _reactDom, _velocityReact, _velocityAnimate, _lodash, _jquery) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _react2 = _interopRequireDefault(_react);
var _reactDom2 = _interopRequireDefault(_reactDom);
var _lodash2 = _interopRequireDefault(_lodash);
var _jquery2 = _interopRequireDefault(_jquery);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var _createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
function _possibleConstructorReturn(self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
//
//*************************
//*************************
// Nonpublished Imports
//
function updateState(ScopeProxy, Parcel) {
var existingState = ScopeProxy.state !== null ? _lodash2.default.cloneDeep(ScopeProxy.state) : {};
var adjustedState = _lodash2.default.merge(existingState, _lodash2.default.cloneDeep(Parcel));
//
try {
ScopeProxy.setState(adjustedState);
} catch (event) {
console.warn("::react-dollyzoom:problem::updateState:", event);
}
}
function watch(Testfunction) {
var watchCore = {
"Match": function Match(Target, Complete, ExpireAt) {
var intervalCount = 0;
var maximumAttempts = ExpireAt !== undefined ? ExpireAt : 2000;
//
var watchInterval = setInterval(function () {
if (Testfunction() === Target) {
Complete();
//
clearInterval(watchInterval);
}
if (intervalCount >= maximumAttempts) {
console.warn("react-dollyzoom.js::watch::exceeded watch limit timeout::action halted.");
//
clearInterval(watchInterval);
}
intervalCount++;
}, 1);
//
}
};
//
return watchCore;
}
//
//*************************
//*************************
// Exports
//
var Dollyzoom = function (_Component) {
_inherits(Dollyzoom, _Component);
//*************************
//*************************
// Standard Methods
//
function Dollyzoom(props) {
_classCallCheck(this, Dollyzoom);
return _possibleConstructorReturn(this, (Dollyzoom.__proto__ || Object.getPrototypeOf(Dollyzoom)).call(this, props));
}
_createClass(Dollyzoom, [{
key: "getChildContext",
value: function getChildContext() {
// empty
}
}, {
key: "getInitialState",
value: function getInitialState() {
return {};
}
}, {
key: "componentWillMount",
value: function componentWillMount() {
// empty
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
// empty
}
}, {
key: "componentDidMount",
value: function componentDidMount() {
var scopeProxy = this;
//
updateState(scopeProxy, {
"Ready": false,
"Panel": {
"Classname": this.props.className
},
"Portal": {
"Distort": {
"Depth": this.props.Portal.Distort.Depth,
"Perspective": this.props.Portal.Distort.Perspective,
"Blurfactor": this.props.Portal.Distort.Blurfactor,
"Fade": this.props.Portal.Distort.Fade,
"Zoom": this.props.Portal.Distort.Zoom
},
"Morph": {
"Apply": {
"Easing": this.props.Portal.Morph.Apply.Easing,
"Duration": this.props.Portal.Morph.Apply.Duration
},
"Restore": {
"Easing": this.props.Portal.Morph.Restore.Easing,
"Duration": this.props.Portal.Morph.Restore.Duration
}
},
"Profile": {
"runOnMount": false
},
"Style": {
"display": "inline-block",
"position": "relative",
"width": "100%",
"height": "100%"
},
"Ready": this.props.Ready,
"Change": this.props.Change,
"Complete": this.props.Complete
},
"Elements": {
//"Style":this.props.Panel.Style,
"Classname": this.props.Panel.Classname
}
});
}
}, {
key: "componentWillUpdate",
value: function componentWillUpdate() {
// empty
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate() {
var scopeProxy = this;
//
window.requestAnimationFrame(function () {
if (scopeProxy.state !== undefined && scopeProxy.state.Ready === false) {
updateState(scopeProxy, {
"Ready": true
});
scopeProxy.renderDescendents();
}
});
}
}, {
key: "render",
value: function render() {
var scopeProxy = this;
var dollyzoompanelClassname = _lodash2.default.has(this, "state.Panel.Classname") ? this.state.Panel.Classname : null;
var dollyzoomportalStyle = _lodash2.default.has(this, "state.Portal.Style") ? this.state.Portal.Style : null;
var dollyzoomelementsStyle = _lodash2.default.has(this, "state.Elements.Style") ? this.state.Elements.Style : null;
var dollyzoomelementsClassname = _lodash2.default.has(this, "state.Elements.Classname") ? this.state.Elements.Classname : null;
var currentTargetSelector = _lodash2.default.has(this, "state.Target.Selector") ? this.state.Target.Selector : null;
var dollyzoomsurrogateDisplay = currentTargetSelector !== null && currentTargetSelector !== undefined ? "inline-block" : "none";
//
var dollyzoomsurrogateStyle = {
"display": dollyzoomsurrogateDisplay,
"position": "absolute",
"top": "0",
"left": "0",
"width": "100%",
"height": "100%"
};
//
var portalmorphProfileOnmount = {
"runOnMount": false
};
//
var portalmorphStyle = {
"display": "block",
"position": "absolute",
"visibility": "hidden",
"opacity": "0",
"top": "0",
"left": "0",
"width": "0",
"height": "0"
};
//
var dollyzoomchildrenStyle = {
"display": "none",
"position": "absolute"
};
//
var portalmorphProfile = _lodash2.default.has(this, "state.Portal.Profile") ? this.state.Portal.Profile : portalmorphProfileOnmount;
//
return _react2.default.createElement(
"div",
{ id: "dollyzoom-panel-container", ref: "dollyzoompanel", className: dollyzoompanelClassname },
_react2.default.createElement(
"div",
{ id: "dollyzoom-elements-containter", ref: "dollyzoomelements", style: dollyzoomelementsStyle, className: dollyzoomelementsClassname },
_react2.default.createElement("div", { id: "dollyzoom-portal-container", ref: "dollyzoomportal", style: dollyzoomportalStyle }),
_react2.default.createElement("div", { id: "dollyzoom-surrogate-container", ref: "dollyzoomsurrogate", style: dollyzoomsurrogateStyle }),
_react2.default.createElement(
"div",
{ id: "dollyzoom-children-container", ref: "dollyzoomchildren", style: dollyzoomchildrenStyle },
this.props.children
)
),
_react2.default.createElement(
_velocityReact.VelocityComponent,
portalmorphProfile,
_react2.default.createElement("div", { id: "portal-morph-container", ref: "portalmorph", style: portalmorphStyle })
)
);
}
}, {
key: "setListeners",
value: function setListeners() {
var scopeProxy = this;
//
}
}, {
key: "renderDescendents",
value: function renderDescendents() {
var scopeProxy = this;
//
watch(function () {
return _typeof(scopeProxy.state);
}).Match("object", function () {
var hostId = scopeProxy.props.Host.Id;
var portalSelectorPath = "#".concat(hostId, " #dollyzoom-portal-container");
var surrogateSelectorPath = "#".concat(hostId, " #dollyzoom-surrogate-container");
var childrenSelectorPath = "#".concat(hostId, " #dollyzoom-children-container");
var portalTargetnode = (0, _jquery2.default)(portalSelectorPath)[0];
var surrogateTargetnode = (0, _jquery2.default)(surrogateSelectorPath)[0];
//
// ********************
// ********************
// nonflux-pattern
// Use jQuery to perform the deep cloning in conjunction with assigning
// the event listeners attached to the content of the dollyzoom elements
// container. Attaching event listeners using jQuery allows jQuery to
// easily deep clone (with listeners) the elements.
var sourceclonePortal = (0, _jquery2.default)(childrenSelectorPath).children(0).clone(true, true)[0];
var sourcecloneSurrogate = (0, _jquery2.default)(childrenSelectorPath).children(0).clone(true, true)[0];
// ********************
// ********************
portalTargetnode.appendChild(sourceclonePortal);
surrogateTargetnode.appendChild(sourcecloneSurrogate);
//
window.requestAnimationFrame(function () {
scopeProxy.state.Portal.Ready({
"children": scopeProxy.props.children
});
});
});
}
}, {
key: "onChange",
value: function onChange(Parcel) {
// empty
}
}, {
key: "dollyzoomApply",
value: function dollyzoomApply(Parcel) {
var scopeProxy = this;
var targetId = Parcel.Targetid;
var onComplete = Parcel.Complete;
var hostId = this.props.Host.Id;
var targetSelector = "#".concat(targetId);
var currentTargetSelector = _lodash2.default.has(this, "state.Target.Selector") ? this.state.Target.Selector : undefined;
var targetSelectorPath = "#".concat(hostId, " #dollyzoom-surrogate-container ", targetSelector);
var portalRef = this.refs.dollyzoomportal;
var surrogateRef = this.refs.dollyzoomsurrogate;
var portalChildelement // get the portal child
= portalRef.firstChild;
var surrogateChildelement // get the descendents child
= surrogateRef.firstChild;
var portalChildelementContent // get the targetElement from within the portal child
= portalChildelement.querySelector(targetSelector);
var surrogateChildelementContent // get the targetElement from within the descendents child
= surrogateChildelement.querySelector(targetSelector);
var initialChildelementScale = 1;
var morphEasing = this.state.Portal.Morph.Apply.Easing;
var morphDuration = this.state.Portal.Morph.Apply.Duration;
var distortDepth = this.state.Portal.Distort.Depth;
var distortDepthUnit = distortDepth.match(/([A-Z,a-z])\w+/g)[0];
var distortBlurfactor = this.state.Portal.Distort.Blurfactor;
var distortBlurUnit = distortBlurfactor.match(/([A-Z,a-z])\w+/g)[0];
var distortPerspective = this.state.Portal.Distort.Perspective;
var distortFade = this.state.Portal.Distort.Fade;
var distortZoom = this.state.Portal.Distort.Zoom;
//
var portalProfile = {
"Profile": {
"duration": morphDuration,
"easing": morphEasing,
"runOnMount": false,
"animation": {
"opacity": 1
},
"progress": function progress(elements, complete, remaining, start, tweenValue) {
// http://velocityjs.org/
// The value of tweenValue is being reported as null for
// unknown reasons. In order to tween the rotation according
// to the easing, the actual value of the opacity must be
// used as it tweens from zero to one. Additionally, at the
// completion of the tween, the value of the opacity is set
// back to zero by Velocity. This must be avoided so that the
// rotation of the sections does not revert to its original
// rotation value.
//
var progressValue = elements[0].style.opacity > 0 ? elements[0].style.opacity : 1;
var translateValue = (parseInt(distortDepth) * progressValue).toString().concat(distortDepthUnit);
var blurValue = (Math.abs(parseInt(distortDepth)) * progressValue * parseFloat(distortBlurfactor)).toString().concat(distortBlurUnit);
var opacityValue = 1 - (1 - distortFade) * progressValue;
var grayscaleValue = ((1 - distortFade) * progressValue * 100).toString().concat("%");
var zoomValue = (parseFloat(distortZoom) - parseFloat(initialChildelementScale)) * progressValue + parseFloat(initialChildelementScale);
var transformValue = "translateZ(".concat(translateValue, ")");
//
updateState(scopeProxy, {
"Portal": {
"Style": {
"display": "inline-block",
"position": "relative",
"transform": transformValue,
"filter": "blur(".concat(blurValue, ") grayscale(", grayscaleValue, ")"),
"opacity": opacityValue
}
}
});
Object.assign(surrogateChildelementContent.style, {
"transform": "scale(".concat(zoomValue.toString(), ")")
});
scopeProxy.state.Portal.Change({
"Style": {
"transform": transformValue,
"filter": "blur(".concat(blurValue, ") grayscale(", grayscaleValue, ")"),
"opacity": opacityValue
},
"Target": {
"Selector": targetSelector,
"transform": "scale(".concat(zoomValue.toString(), ")")
}
});
},
"complete": function complete(event) {
scopeProxy.state.Portal.Complete({
"Action": "dollyzoomApply",
"Target": targetSelector
});
onComplete(document.querySelector(targetSelectorPath));
}
}
};
//
if (currentTargetSelector !== null && currentTargetSelector !== undefined) {
this.dollyzoomRestore(currentTargetSelector.split("#")[1]);
} else {
Object.assign(portalChildelementContent.style, {
"visibility": "hidden" // hide targetElement within the dollyzoomPortal element
});
Object.assign(surrogateChildelement.style, {
"visibility": "hidden" // hides all child elements of the dollyzoomDescendents element
});
Object.assign(surrogateChildelementContent.style, {
"visibility": "visible" // forces targetElement to be visible within the dollyzoomDescendents element
});
updateState(scopeProxy, {
"Portal": portalProfile,
"Elements": {
"Style": {
"display": "inline-block",
"position": "relative",
"transform-style": "preserve-3d",
"perspective": distortPerspective
}
},
"Target": {
"Selector": targetSelector
}
});
}
}
}, {
key: "dollyzoomRestore",
value: function dollyzoomRestore() {
var scopeProxy = this;
var hostId = this.props.Host.Id;
var targetSelector = this.state.Target.Selector;
var currentTargetSelector = this.state.Target.Selector;
var targetSelectorPath = "#".concat(hostId, " #dollyzoom-surrogate-container ", targetSelector);
var portalRef = this.refs.dollyzoomportal;
var surrogateRef = this.refs.dollyzoomsurrogate;
var portalChildelement // get the portal child
= portalRef.firstChild;
var surrogateChildelement // get the descendents child
= surrogateRef.firstChild;
var portalChildelementContent // get the targetElement from within the portal child
= portalChildelement.querySelector(targetSelectorPath);
var surrogateChildelementContent // get the targetElement from within the descendents child
= surrogateChildelement.querySelector(targetSelectorPath);
var initialChildelementScale = 1;
var morphEasing = this.state.Portal.Morph.Restore.Easing;
var morphDuration = this.state.Portal.Morph.Restore.Duration;
var distortDepth = this.state.Portal.Distort.Depth;
var distortDepthUnit = distortDepth.match(/([A-Z,a-z])\w+/g)[0];
var distortBlurfactor = this.state.Portal.Distort.Blurfactor;
var distortBlurUnit = distortBlurfactor.match(/([A-Z,a-z])\w+/g)[0];
var distortPerspective = this.state.Portal.Distort.Perspective;
var distortFade = this.state.Portal.Distort.Fade;
var distortZoom = this.state.Portal.Distort.Zoom;
//
var portalProfile = {
"Profile": {
"duration": morphDuration,
"easing": morphEasing,
"runOnMount": false,
"animation": {
"opacity": 0
},
"progress": function progress(elements, complete, remaining, start, tweenValue) {
// http://velocityjs.org/
// The value of tweenValue is being reported as null for
// unknown reasons. In order to tween the rotation according
// to the easing, the actual value of the opacity must be
// used as it tweens from zero to one. Additionally, at the
// completion of the tween, the value of the opacity is set
// back to zero by Velocity. This must be avoided so that the
// rotation of the sections does not revert to its original
// rotation value.
//
var progressValue = elements[0].style.opacity > 0 ? elements[0].style.opacity : 0;
var translateValue = (parseInt(distortDepth) * progressValue).toString().concat(distortDepthUnit);
var blurValue = (Math.abs(parseInt(distortDepth)) * progressValue * parseFloat(distortBlurfactor)).toString().concat(distortBlurUnit);
var opacityValue = 1 - (1 - distortFade) * progressValue;
var grayscaleValue = ((1 - distortFade) * progressValue * 100).toString().concat("%");
var zoomValue = (parseFloat(distortZoom) - parseFloat(initialChildelementScale)) * progressValue + parseFloat(initialChildelementScale);
var transformValue = "translateZ(".concat(translateValue, ")");
//
updateState(scopeProxy, {
"Portal": {
"Style": {
"display": "inline-block",
"position": "relative",
"transform": transformValue,
"filter": "blur(".concat(blurValue, ") grayscale(", grayscaleValue, ")"),
"opacity": opacityValue
}
}
});
Object.assign(surrogateChildelementContent.style, {
"transform": "scale(".concat(zoomValue.toString(), ")")
});
scopeProxy.state.Portal.Change({
"Style": {
"transform": transformValue,
"filter": "blur(".concat(blurValue, ") grayscale(", grayscaleValue, ")"),
"opacity": opacityValue
},
"Target": {
"Selector": targetSelector,
"transform": "scale(".concat(zoomValue.toString(), ")")
}
});
},
"complete": function complete(event) {
var hostId = scopeProxy.props.Host.Id;
var portalSelectorPath = "#".concat(hostId, " #dollyzoom-portal-container");
var surrogateSelectorPath = "#".concat(hostId, " #dollyzoom-surrogate-container");
// ********************
// ********************
// nonflux-pattern
// Use jQuery to perform the deep cloning in conjunction with assigning
// the event listeners attached to the content of the dollyzoom elements
// container. Attaching event listeners using jQuery allows jQuery to
// easily deep clone (with listeners) the elements.
var surrogateContent = (0, _jquery2.default)(surrogateSelectorPath).clone(true, true);
//
(0, _jquery2.default)(portalSelectorPath).html((0, _jquery2.default)(surrogateContent).children());
//
(0, _jquery2.default)(portalSelectorPath).children(0).css("visibility", "inherit");
// ********************
// ********************
//
Object.assign(surrogateChildelementContent.style, {
"visibility": "inherit" // reset visibility to default flow
});
updateState(scopeProxy, {
"Target": {
"Selector": null
}
});
scopeProxy.state.Portal.Complete({
"Action": "dollyzoomRestore",
"Target": targetSelector
});
}
}
};
//
if (targetSelector !== null) {
updateState(scopeProxy, {
"Portal": portalProfile,
"Elements": {
"Style": {
"display": "inline-block",
"position": "relative",
"transform-style": "preserve-3d",
"perspective": distortPerspective
}
}
});
} else {
console.warn("react-dollyzoom.js::dollyzoomRestort::dollyzoomRestore called before dollyzoomApply::action halted.");
}
}
}]);
return Dollyzoom;
}(_react.Component);
Dollyzoom.contextTypes = {}
// empty
//
;
exports.default = Dollyzoom;
});