spherical effect

Make spherical transition effect by ReactJS

Have you ever seen the spherical transition effect? This effect you will see on menu, photo gallery. When you hit next or previous button, every elements will transition on the slice of sphere.

First, you need to write the following html page:

<!DOCTYPE html>
<html >
<head>
    <link rel='stylesheet prefetch' href='https://cdnjs.cloudflare.com/ajax/libs/foundicons/3.0.0/foundation-icons.css'>
    <link rel='stylesheet prefetch' href='https://cdnjs.cloudflare.com/ajax/libs/foundicons/3.0.0/svgs/fi-list.svg'>
    <link rel="stylesheet" href="style.css">
</head>

<body>
  <div id="app"></div>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react-with-addons.min.js'></script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react-dom.min.js'></script>
    <script src="index.js"></script>
</body>
</html>

The foundation-icon.css and fi-list.svg will be used for graphical element on menu (icons). The most important is the react library. In this, we use react-with-addon and react-dom.

Next we will create the files style.css and index.js. For the style.css file:

@import url("https://fonts.googleapis.com/css?family=Lobster");
body {
  background-color: #89FAD0;
  font-family: 'Lobster';
}

#carousel {
  position: absolute;
  height: 200px;
  width: 810px;
  margin: auto;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
}

.arrow {
  position: absolute;
  width: 30px;
  height: 30px;
  background-color: white;
  text-align: center;
  font-size: 25px;
  border-radius: 50%;
  cursor: pointer;
  font-size: 20px;
  color: #228291;
  line-height: 30px;
  margin-top: 85px;
  z-index: 1000;
}

.arrow-right {
  left: 780px;
}

.item {
  text-align: center;
  color: white;
  font-size: 40px;
  position: absolute;
  transition: height 1s, width 1s, left 1s, margin-top 1s, line-height 1s, background-color 1s;
}

.level-2 {
  height: 150px;
  width: 110px;
  line-height: 150px;
  background-color: #228291;
  left: 650px;
  margin-top: 25px;
}

.level-1 {
  height: 180px;
  width: 130px;
  line-height: 180px;
  background-color: #6796E5;
  left: 500px;
  margin-top: 10px;
}

.level0 {
  height: 200px;
  width: 150px;
  line-height: 200px;
  background-color: #4EC9E1;
  left: 330px;
}

.level1 {
  height: 180px;
  width: 130px;
  line-height: 180px;
  background-color: #6796E5;
  margin-top: 10px;
  left: 180px;
}

.level2 {
  height: 150px;
  width: 110px;
  line-height: 150px;
  background-color: #228291;
  margin-top: 25px;
  left: 50px;
}

.left-enter {
  opacity: 0;
  left: -60px;
  height: 120px;
  width: 90px;
  line-height: 120px;
  margin-top: 40px;
}

.left-enter.left-enter-active {
  opacity: 1;
  left: 50px;
  height: 150px;
  width: 110px;
  line-height: 150px;
  margin-top: 25px;
  transition: left 1s, opacity 1s, height 1s, width 1s, margin-top 1s, line-height 1s;
}

.left-leave {
  opacity: 1;
  left: 650px;
  height: 150px;
  width: 110px;
  line-height: 150px;
  margin-top: 25px;
}

.left-leave.left-leave-active {
  left: 780px;
  opacity: 0;
  height: 120px;
  line-height: 120px;
  margin-top: 40px;
  width: 90px;
  transition: left 1s, opacity 1s, height 1s, width 1s, margin-top 1s, line-height 1s;
}

.right-enter {
  opacity: 0;
  left: 760px;
  height: 120px;
  width: 90px;
  line-height: 120px;
  margin-top: 40px;
}

.right-enter.right-enter-active {
  left: 650px;
  opacity: 1;
  height: 150px;
  margin-top: 25px;
  line-height: 150px;
  width: 110px;
  transition: left 1s, opacity 1s, height 1s, width 1s, margin-top 1s, line-height 1s;
}

.right-leave {
  left: 50px;
  height: 150px;
  opacity: 1;
  margin-top: 25px;
  line-height: 150px;
  width: 110px;
}

.right-leave.right-leave-active {
  left: -60px;
  opacity: 0;
  height: 120px;
  width: 90px;
  line-height: 120px;
  margin-top: 40px;
  transition: left 1s, opacity 1s, height 1s, width 1s, margin-top 1s, line-height 1s;
}

.noselect {
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

For index.js:

'use strict';

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

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; }

var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;

var Carousel = function (_React$Component) {
    _inherits(Carousel, _React$Component);

    function Carousel(props) {
        _classCallCheck(this, Carousel);

        var _this = _possibleConstructorReturn(this, _React$Component.call(this, props));

        _this.state = {
            items: _this.props.items,
            active: _this.props.active,
            direction: ''
        };
        _this.rightClick = _this.moveRight.bind(_this);
        _this.leftClick = _this.moveLeft.bind(_this);
        return _this;
    }

    Carousel.prototype.generateItems = function generateItems() {
        var items = [];
        var level;
        console.log(this.state.active);
        for (var i = this.state.active - 2; i < this.state.active + 3; i++) {
            var index = i;
            if (i < 0) {
                index = this.state.items.length + i;
            } else if (i >= this.state.items.length) {
                index = i % this.state.items.length;
            }
            level = this.state.active - i;
            items.push(React.createElement(Item, { key: index, id: this.state.items[index], level: level }));
        }
        return items;
    };

    Carousel.prototype.moveLeft = function moveLeft() {
        var newActive = this.state.active;
        newActive--;
        this.setState({
            active: newActive < 0 ? this.state.items.length - 1 : newActive,
            direction: 'left'
        });
    };

    Carousel.prototype.moveRight = function moveRight() {
        var newActive = this.state.active;
        this.setState({
            active: (newActive + 1) % this.state.items.length,
            direction: 'right'
        });
    };

    Carousel.prototype.render = function render() {
        return React.createElement(
            'div',
            { id: 'carousel', className: 'noselect' },
            React.createElement(
                'div',
                { className: 'arrow arrow-left', onClick: this.leftClick },
                React.createElement('i', { className: 'fi-arrow-left' })
            ),
            React.createElement(
                ReactCSSTransitionGroup,
                {
                    transitionName: this.state.direction },
                this.generateItems()
            ),
            React.createElement(
                'div',
                { className: 'arrow arrow-right', onClick: this.rightClick },
                React.createElement('i', { className: 'fi-arrow-right' })
            )
        );
    };

    return Carousel;
}(React.Component);

var Item = function (_React$Component2) {
    _inherits(Item, _React$Component2);

    function Item(props) {
        _classCallCheck(this, Item);

        var _this2 = _possibleConstructorReturn(this, _React$Component2.call(this, props));

        _this2.state = {
            level: _this2.props.level
        };
        return _this2;
    }

    Item.prototype.render = function render() {
        var className = 'item level' + this.props.level;
        return React.createElement(
            'div',
            { className: className },
            this.props.id
        );
    };

    return Item;
}(React.Component);

var items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
ReactDOM.render(React.createElement(Carousel, { items: items, active: 0 }), document.getElementById('app'));

Finally, you get the spherical transition effect as cover image. You can test the result at CodePen.

Thank you Andy Pag├Ęs for this amazing effect.