cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - ActiveLovehtmlicon-new-collectionicon-personicon-teamlog-outoctocatpop-outspinnerstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

You're using npm packages, so we've auto-selected Babel for you here, which we require to process imports and make it all work. If you need to use a different JavaScript preprocessor, remove the packages in the npm tab.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Use npm Packages

We can make npm packages available for you to use in your JavaScript. We use webpack to prepare them and make them available to import. We'll also process your JavaScript with Babel.

⚠️ This feature can only be used by logged in users.

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

            
              
<div class="container">
    <section class="canvas-container">
        <canvas id="collageCanvas" class="collage-canvas" width="800" height="600"></canvas>
    </section>

    <section class="items-container">
        <div class="row">
            <span>Choose Environment: </span>
            <div class="control">
                <span>background:</span>
                <span class="environment fa-cloud fa" data-url="sky.jpg" data-type="background"></span>
                <span class="environment fa fa-moon-o" data-url="space.jpg" data-type="background"></span>
            </div>
            <div class="control">
                <span>foreground: </span>
                <span class="environment fa fa-tree" data-url="grass.jpg" data-type="foreground"></span>
                <span class="environment fa fa-asterisk" data-url="snow.jpg" data-type="foreground"></span>
                <span class="environment fa fa-ban" data-url="" data-type="background"></span>
            </div>
            <div class="control">
                <span>Organisms</span>
                <span class="item fa fa-paw" data-url="coyote.png"></span>
                <span class="item fa fa-bug" data-url="dragonfly.png"></span>
            </div>
        </div>
    </section>
    <section class="controls-container">

        <div class="row">
            <span>Manipulate Selected Item: </span>

            <div class="control">
                <span class="fa fa-expand scale"></span>
            </div>
            <div class="control">
                <span class="fa fa-rotate-right rotate"></span>
            </div>
            <div class="control">
                <span class="fa fa-eye blur"></span>
            </div>
            <div class="control">
                <span class="front fa fa-sort"></span>
            </div>
            <div class="control">
                <span class="remove fa fa-trash"></span>
            </div>
            <div class="tooltip"></div>
        </div>
    </section>
    <section class="save-container">
        <button class="save">SAVE IMAGE</button>
        <div class="image-container"></div>
    </section>
</div>

<div class="language-container" style="display: none;">
    <ul>
        <li class="lang-item" data-id="cleanText">Please make sure your responses are complete and contain no profanity</li>
        <li class="lang-item" data-id="selectImageText">Please select an image on the canvas first</li>
        <li class="lang-item" data-id="fgName">ground</li>
        <li class="lang-item" data-id="bgName">sky</li>
    </ul>
</div>

            
          
!
            
              * {
    box-sizing: border-box;
}

body {
    font-family: "Trebuchet MS","Lucida Grande","Lucida Sans Unicode","Lucida Sans",Tahoma,sans-serif
}

h1,h2,h3,h4,h5 {
    font-weight: normal;
    margin: 0;
}

button {
    border-radius: 0;
    border: 1px solid #ddd;
    background-color: #fcfcfc;
}

section {
    padding: 10px;
    border-top: 1px solid #ddd;
}

section.items-container, section.canvas-container {
    border-top: 0;
}

section .row:first-child {
    margin-top: 0;
}

img {
    max-width: 100%;
}

.container {
    max-width: 800px;
    margin: 0 auto;
    width: 100%;
}

.collage-canvas {
    border: 1px solid #ddd;
    width: 100%;
    height: 100%;
}

.control {
    display: inline-block;
    margin-right: 20px;
    padding: 10px;
}

.row {
    margin-top: 20px;
}

.row.control:last-child {
    margin-right: 0px;
}

.save-container {
    text-align: center;
}

.save {
    height: 50px;
    width: 150px;
}

.image-container {
    margin-top: 20px;
    width: 300px;
    margin-left: auto;
    margin-right: auto;
}

.control > .fa {
    cursor: pointer;
}

.control > .fa:hover {
    color: red;
}

.tooltip {
    color: red;
}

            
          
!
            
              var collage = {};
var collageModule;

var langImage = {}; //will hold the language from language files to display within js

collage.main = function() {
    var self = this;

    var stage = null; //will hold all canvas references
    var stageBounds = { width: 800, height: 600, midX: null, midY: null }
    var stageUpdate = false; //tells stage when to update

    var gridImg, gridBitmap = null; //perspective plane
    var gridBounds; //holds Y pos of grid

    //var organismImgArray = [];
    //var organismBitmapArray = [];
    var organismPath = 'https://s3.amazonaws.com/codepen-mh/collage/animals/';
    var environmentPath = 'https://s3.amazonaws.com/codepen-mh/collage/environment/';

    var envForeground = null; //ref to the foreground bitmap
    var envBackground = null; //ref to the background bitmap

    var editItem; //reference to the bitmap being manipulated
    var editMode; //scale, move, etc
    var blurFilter = null;

    var isTouch; //will determine how to handle button press / hold

    var scaleInterval, blurInterval, rotateInterval; //holds timer to do repeat scale calls on hold
    var scaleMore = false; //make image larger when true, smaller when false
    var blurMore = true; //make more blurry when true, less blurry when false
    var blurHold = false; //true when user first holds mouse down, false when they release

    var disabledTimer; //holds the timer to hide the tooltip for disabled buttons

    var ww, wh;

    var configObj = {
      "borderWidth": 5,
      "borderOffsetX": 5,
      "borderOffsetY": 5,
      "borderBlur": 0,
      "borderColor": "#e23e42",
      "blurLimit": 10,
      "rotateTiming": 10,
      "rotateStep": 5,
      "scaleTiming": 10,
      "minScale": 0.25,
      "maxScale": 1.5,
      "scaleStep": 0.01,
      "blurTiming": 100,
      "disabledMessageTime": 200000,
      "stageWidth": 800,
      "stageHeight": 800
    }

    var $canvasControls = $('.controls-container');


    self.init = function() {
        isTouch = detectTouch();
        initStage();
        getLanguage();
    }

    var getLanguage = function(){
        $('.lang-item').each(function(){
            var $thisItem = $(this);
            var thisID = $thisItem.attr('data-id');
            var thisText = $thisItem.text();
            langImage[thisID] = thisText;
        });
    }

    var detectTouch = function(){
        if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
            return true;
        } else {
            return false;
        }
    }



    var initStage = function(){
        stage = new createjs.Stage("collageCanvas");
        stageBounds.midX = stageBounds.width/2;
        stageBounds.midY = stageBounds.height/2;

        if (createjs.Touch.isSupported()) {
            createjs.Touch.enable(stage);
        }

        ww = $(window).width();
        wh = $(window).height();

        loadGrid();
    }

    var loadGrid = function(){
        gridImg = new Image();
        gridImg.crossOrigin = "Anonymous";
        gridImg.src = "https://s3.amazonaws.com/codepen-mh/collage/environment/perspective-grid.png";
      

        $(gridImg).load(function(){
            drawGrid();
        });
    }

    var drawGrid = function(){
        gridBitmap = new createjs.Bitmap(gridImg);
        stage.addChild(gridBitmap);
        gridBounds = gridBitmap.getBounds();
        gridBounds.y = stageBounds.height - gridBounds.height;
        gridBitmap.x = 0;
        gridBitmap.y = gridBounds.y;
        stage.update();
        addTick();
        addFilters();
        addControlsListeners();
    }

    var addFilters = function(){
        blurFilter = new createjs.BlurFilter(0, 0, 2); //x,y,quality (1,2,3,etc);
    }

    var loadOrganism = function(src){
        var orgImg = new Image();
        orgImg.crossOrigin = "Anonymous";
        orgImg.src = src;

        $(orgImg).load(function(){
            //organismImgArray.push(orgImg);
            drawOrganism(orgImg);
        });
    }

    var loadEnvironment = function(src,type){
        var envImg = new Image();
        envImg.crossOrigin = "Anonymous";
        envImg.src = src;

        $(envImg).load(function(){
            drawEnvironment(envImg,type);
        });
    }

    var drawOrganism = function(orgimg){
        var orgBitmap = new createjs.Bitmap(orgimg);
        orgBitmap.cache(0, 0, orgimg.width, orgimg.height);
        stage.addChild(orgBitmap);
        self.centerElement(orgBitmap);
        //editItem = orgBitmap;
        addBitmapListeners(orgBitmap);
        //organismBitmapArray.push(orgBitmap);
        stageUpdate = true;
    }

    var drawEnvironment = function(envImg,type){
        var envBitmap = new createjs.Bitmap(envImg);
        switch (type){
            case 'foreground':
                envBitmap.y = gridBounds.y;
                if (envForeground != null){
                    stage.removeChild(envForeground);
                }
                envForeground = envBitmap;
                stage.addChild(envBitmap);
                stage.setChildIndex( envBitmap, 0);
                break;
            case 'background':
                envBitmap.y = 0;
                if (envBackground != null){
                    stage.removeChild(envBackground);
                }
                envBackground = envBitmap;
                stage.addChild(envBitmap);
                stage.setChildIndex( envBitmap, 1);
                break;
        }

        //addBitmapListeners(orgBitmap);
        stageUpdate = true;
    }

    var addBitmapListeners = function(bitmap){
        bitmap.on("mousedown", onBitmapDown);
        bitmap.on('click',toggleBitmapManipulation);
        bitmap.on("pressmove",onDragBitmap);
        bitmap.on('pressup',onDragUp);
    }

    var addTick = function(){
        createjs.Ticker.addEventListener("tick", onTick);
    }

    var addControlsListeners = function(){
        /*$('.scale').on('input',onScaleChange);
        $('.blur').on('input',onBlurChange);
        $('.rotate').on('input',onRotateChange);*/
        $('.front').on('click',onFrontClick);
        $('.remove').on('click',onRemoveClick);
        $('.item').on('click',onItemClick);
        $('.environment').on('click',onEnvironmentClick);
        $('.save').on('click',onSaveClick);

        if (isTouch){ //buttons behave differently for touch devices as a long press by default fires a right click in touch devices, thus user must tap these repeatedly to repeat the event
            $('.rotate').on('click',updateRotate);
            $('.scale').on('click',updateScale);
            $('.blur').on('click',changeBlur);
        } else {
            $('.rotate').on('mousedown',onRotateDown); //start pressing
            $('.rotate').on('mouseup',onRotateUp); //stop pressing
            $('.scale').on('mousedown',onScaleDown);
            $('.scale').on('mouseup',onScaleUp);
            $('.blur').on('mousedown',onBlurDown);
            $('.blur').on('mouseup',onBlurUp);
        }

        initCanvasTouch();

    }

    var onItemClick = function(){
        var $thisItem = $(this);
        var thisURL = $thisItem.attr('data-url');
        var thisImageURL = organismPath + thisURL;
        loadOrganism(thisImageURL);
    }

    var onEnvironmentClick = function(){
        var $thisItem = $(this);
        var thisURL = $thisItem.attr('data-url');
        if (thisURL == ""){
            removeForeground();
        } else {
            var thisType = $thisItem.attr('data-type');
            var thisImageURL = environmentPath + thisURL;
            loadEnvironment(thisImageURL,thisType);
        }
    }

    var onBitmapDown = function(e){
        //this.parent.addChild(this); //MH - will move to front of z order
        this.offset = {x: this.x - e.stageX, y: this.y - e.stageY};
    }

    var onDragBitmap = function(e){
        if (this.editable){
            this.dragging = true;
            this.x = e.stageX + this.offset.x;
            this.y = e.stageY + this.offset.y;
            stageUpdate = true;
        }

    }

    var onDragUp = function(){
        this.dragging = false;
    }

    var onScaleChange = function(){
        if (editItem.editable){
            var $thisSlider = $(this);
            var thisVal = $thisSlider.val();
            editItem.scaleX = editItem.scaleY = thisVal;
            stageUpdate = true;
        }

    }

    var onFrontClick = function(){
        bringToFront(editItem);
    }

    var onRemoveClick = function(){
        var itemToRemove = editItem;
        editItem = null;
        itemToRemove.removeAllEventListeners();
        stage.removeChild(itemToRemove); //MH - need to first remove item and image from array (or delete arrays if they're not being used)
    }

    var removeForeground = function(){
        envForeground = null;
        stage.removeChildAt(0);
    }

    var onBlurChange = function(){

        if (editItem != null && editItem.editable){
            var $thisSlider = $(this);
            var thisVal = $thisSlider.val();

            if (editItem.filters == null){
                editItem.filters = [blurFilter];
            }

            editItem.filters[0].blurX = editItem.filters[0].blurY = thisVal;

            stageUpdate = true;
            editItem.updateCache();
        }

    }

    var onRotateChange = function(){
        if (editItem != null && editItem.editable){
            var $thisSlider = $(this);
            var thisVal = $thisSlider.val();
            editItem.rotation = thisVal;
            stageUpdate = true;
        }
    }

    var toggleBitmapManipulation = function(e){

        if (editItem && editItem.id != this.id){ //we are switching to a new item
            editItem.editable = false;
            removeGlow(editItem);
            editItem = this;
            adjustSliderValues();
        }

        if (this.editable){ //item is editable if it has been selected
            if (!this.dragging){
                this.editable = false;
                removeGlow(this);
                editItem = null;
            }
        } else {
            editItem = this;
            this.editable = true;
            addGlow(this);
        }
    }

    var adjustSliderValues = function(){
        var thisScale = editItem.scaleX;
        var thisRotation = editItem.rotation;
        if (editItem.filters !== null){
            var thisBlur = editItem.filters[0].blurX;
        } else {
            var thisBlur = 0;
        }
        $('.scale').val(thisScale);
        $('.blur').val(thisBlur);
        $('.rotate').val(thisRotation);
    }

    var addGlow = function(bitmap){
         bitmap.shadow = new createjs.Shadow("#ff0000", 0, 0, 5);
         stageUpdate = true;
         enableCanvasTouch();
         showCanvasControls();
    }

    var removeGlow = function(bitmap){
        bitmap.shadow = null;
        stageUpdate = true;
        disableCanvasTouch(); //remove touch functionality for the item
        hideCanvasControls();
    }

    var showCanvasControls = function(){
        $canvasControls.addClass('active');
    }

    var hideCanvasControls = function(){
        $canvasControls.removeClass('active');
    }

    var initCanvasTouch = function(){ //add the listeners that allow a user to use touch gestures to rotate and scale their imagery
        var touchEl = document.getElementById('collageCanvas');
        var touchOptions = {};
        touchInstance = new Hammer.Manager(touchEl);
        pinch = new Hammer.Pinch();
        rotate = new Hammer.Rotate();
        pinch.recognizeWith(rotate);
        touchInstance.add([pinch, rotate]);
        touchInstance.on("pinch rotate",onCanvasTouch); //add the ability to recognize pinch and rotate...
        disableCanvasTouch(); //...but disable it for now
    }

    var onCanvasTouch= function(e){
        var rotationVal = e.rotation;
        var scaleVal = e.scale;
        editItem.rotation = rotationVal;
        editItem.scaleX = editItem.scaleY = scaleVal;
        stageUpdate = true;
    }

    var enableCanvasTouch = function(){
        touchInstance.get('rotate').set({enable: true });
        touchInstance.get('pinch').set({enable: true });
    }

    var disableCanvasTouch = function(){
        touchInstance.get('rotate').set({enable: false });
        touchInstance.get('pinch').set({enable: false });
    }

    var removeHighlight = function(item){

        hideCanvasControls();
        if (item){
            item.shadow = null;
        }

        stageUpdate = true;
    }

    self.centerElement = function(el){
        el.regX = el.image.width >> 1;
        el.regY = el.image.height >> 1;
        el.x = stageBounds.width >> 1;
        el.y = stageBounds.height >> 1;
    }

    var bringToFront = function(obj){
        stage.setChildIndex( obj, stage.getNumChildren()-1);
    }

    var onTick = function(event) {
        if (stageUpdate) {
            !stageUpdate; // only update once
            stage.update(event);
        }
    }

    var onSaveClick = function(){
        var collage = document.getElementById("collageCanvas");
        var collageImg = convertCanvasToImage(collage);
        $('.image-container').html(collageImg);
        //saveImageToDrive();
    }

    var saveImageToDrive = function(){
        var url = 'save.php',
        data = $('.image-container').find('img').attr('src');

        $.ajax({
            type: "POST",
            url: url,
            dataType: 'text',
            data: {
                base64data : data
            }
        });
    }

    var convertCanvasToImage = function(canvas) {
        var image = new Image();
        image.crossOrigin = "Anonymous";
        image.src = canvas.toDataURL("image/png");
        return image;
    }

    var onRotateChange = function(){
        if (editItem != null && editItem.editable){
            var $thisSlider = $(this);
            var thisVal = $thisSlider.val();
            editItem.rotation = thisVal;
            stageUpdate = true;
        }
    }

    var onRotateDown = function(){
        var isActive = $canvasControls.hasClass('active');
        var $thisButton = $(this);
        var $thisContainer = $thisButton.closest('.controls-container');

        if (isActive){
            $thisButton.addClass('active');
            rotateInterval = setInterval(updateRotate,configObj.rotateTiming); //and repeat the call if they are holding the button down
            deactivateFunction = onRotateUp;
            watchMouse();
        } else {
            showDisabledMessage($thisContainer);
        }
    }

    var onRotateUp = function(e){
        $('.rotate').removeClass('active');
        clearInterval(rotateInterval);
        stopWatchingMouse();
    }

    var updateRotate = function(){
        if (editItem != null && editItem.editable){
        //var editItemImg = editItem.children[0];
            if (editItem.rotation < 360){
                editItem.rotation++;
            } else {
                editItem.rotation = 0;
            }
            stageUpdate = true;
        }
    }

    var onScaleDown = function(){
        var isActive = $canvasControls.hasClass('active');
        var $thisButton = $(this);
        var $thisContainer = $thisButton.closest('.controls-container');

        if (isActive){
            $thisButton.addClass('active');
            scaleInterval = setInterval(updateScale,configObj.scaleTiming);
            deactivateFunction = onScaleUp;
            watchMouse();
        } else {
            showDisabledMessage($thisContainer);
        }
    }

    var onScaleUp = function(){
        $('.scale').removeClass('active');
        clearInterval(scaleInterval);
        stopWatchingMouse();
    }

    var updateScale = function(){
        if (editItem != null && editItem.editable){
            if (scaleMore){
                if (editItem.scaleX < configObj.maxScale){
                    scaleVal = editItem.scaleX + configObj.scaleStep;
                } else {
                    scaleMore = false;
                    $('.scale').removeClass('up');
                }
            } else {
                if (editItem.scaleX > configObj.minScale){
                    scaleVal = editItem.scaleX - configObj.scaleStep;
                } else {
                    scaleMore = true;
                    $('.scale').addClass('up');
                }
            }

            editItem.scaleX = editItem.scaleY = scaleVal;
            stageUpdate = true;
        }
    }

    var onBlurDown = function(){
        var isActive = $canvasControls.hasClass('active');
        var $thisButton = $(this);
        var $thisContainer = $thisButton.closest('.controls-container');

        if (isActive){
            $thisButton.addClass('active');
            changeBlur(); //change the blur
            deactivateFunction = onBlurUp;
            blurInterval = setInterval(changeBlur,configObj.blurTiming); //and repeat the call if they are holding the button down
            watchMouse();
        } else {
            showDisabledMessage($thisContainer);
        }

    }

    var onBlurUp = function(){
        $('.blur').removeClass('active');
        clearInterval(blurInterval);
        stopWatchingMouse();
    }

    var changeBlur = function(){
        if (editItem != null && editItem.editable){
            var blurVal = getBlur();
            if (blurMore){
                if (blurVal < configObj.blurLimit){
                    blurVal++;
                } else { //we're at max bluriness, make successive calls blur less
                    $('.blur').removeClass('blurry');
                    blurMore = false;
                }
            } else {
                if (blurVal > 0){
                    blurVal--;
                } else { //we are unblurred, make successive calls blur more
                    $('.blur').addClass('blurry');
                    blurMore = true;
                }

            }
            updateBlur(blurVal);
        }
    }

    var getBlur = function(){

        //var editItemImg = editItem.children[0];
        var editItemImg = editItem;

        if (editItemImg.filters == null){
            editItemImg.filters = [blurFilter];
        }
        return editItemImg.filters[0].blurX;
    }

    var onBlurChange = function(){

        if (editItem != null && editItem.editable){
            var $thisSlider = $(this);
            var thisVal = $thisSlider.val();
            updateBlur(thisVal);
        }

    }

    var updateBlur = function(thisVal){
        //var editItemImg = editItem.children[0];
        var editItemImg = editItem;


        if (editItemImg.filters == null){
            editItemImg.filters = [blurFilter];
        }

        editItemImg.filters[0].blurX = editItemImg.filters[0].blurY = thisVal;

        stageUpdate = true;
        editItemImg.updateCache();
    }



    var watchMouse = function(){ //need to make sure that if a user has held the mouse down but then moves the mouse off screen, that we disable whatever action was occurring as they held the mouse down
        $(window).mousemove(onTrackMouse);
    }

    var stopWatchingMouse = function(){
        $(window).off('mousemove');
    }

    var onTrackMouse = function(e){ //need to make sure that if a user has held the mouse down but then moves the mouse off screen, that we disable whatever action was occurring as they held the mouse down
        if (e.pageX > ww - 10 || e.pageY > wh - 10 ){
            deactivateFunction();
        }
    }

    var showDisabledMessage = function($thisButton){ //the button cannot be utilized as there is no item selected on the canvas
        var content = langImage.selectImageText;
        $('.tooltip').html(content);
        clearTimeout(disabledTimer);
        disabledTimer = setTimeout(hideDisabledMessage,configObj.disabledMessageTime);
    }

    var onShowDisabledMessage = function(){
        //clearTimeout(disabledTimer);
        //disabledTimer = setTimeout(hideDisabledMessage,configObj.disabledMessageTime);
    }

    var hideDisabledMessage = function(){
        $('.tooltip').html('');
    }

};

$(document).ready(function(){
    collageModule= new collage.main();
    collageModule.init();
});



            
          
!
999px
🕑 One or more of the npm packages you are using needs to be built. You're the first person to ever need it! We're building it right now and your preview will start updating again when it's ready.
Loading ..................

Console