Issue #1368Opened August 16, 2018by shlomoko2 reactions

Resize canvas

Question

Is there a way to have a dynamic resizing of the canvas? i.e not using the Device Manager, but having a drag and drop of the width, similar to what you can do with panels or components?

Thanks

Answers (3)

jvillenaNovember 7, 20182 reactions

@artf and @shlomoko, I will explain the step little by little. Firstly, I need to use Jquery but you can use other library or integrate inside the GrapesJS engine. In my case I wanted to do it transparent to the engine.

  1. I have two hidden div element in the main index HTML outside of the iframe that grapesJS is generated in runtime:
<!-- DEVICE RESOLUTION HINTS-->
<div class="device-resolution hidden">
    <div class="dr-type">Phone - Landscape</div>
    <div class="dr-measure">Affects 767px and below"</div>
</div>

<div class="iframe-handle-container hidden">
    <div class="handle right-handle">
         <div class="gutter-handle"></div>
         <div class="tab-handle"></div>
         <div class="dim-indicator"></div>
         <div class="js-mobile-list  mobile-wrapper">
              .....
        </div>

    </div>
    <div class="handle left-handle"></div>

</div>
  • The first div called "device-resolution" will show the device resolution compatible with this screen size.
  • The second div contains: -- The gutter -- The left and right vertical bar that wrap the body when the gutter is pressed

The following piece of code is the stylesheet used:

/* RESEZING GLUTTER */
.iframe-handle-container .handle {
    position: absolute;
    top: 0;
    bottom: 0;
    z-index: 14;
}
.iframe-handle-container .handle:before {
    content: "";
    position: absolute;
    top: 0;
    left: -3px;
    width: 4px;
    height: 100%;
    background: #178df7;
    display: none;
}
.iframe-handle-container .right-handle {
    left: -1px;
    width: 14px;
}
.iframe-handle-container .left-handle {
    left: -1px;
    width: 3px;
    pointer-events: all;
    cursor: col-resize;
}
.iframe-handle-container .tab-handle {
    position: absolute;
    top: 50%;
    width: 14px;
    height: 38px;
    margin-top: -19px;
    background: #808080;
    cursor: col-resize;
    pointer-events: all;
    border-top-right-radius: 3px;
    border-bottom-right-radius: 3px;
}
.iframe-handle-container .tab-handle:before,
.iframe-handle-container .tab-handle:after {
    content: "";
    position: absolute;
    top: 8px;
    bottom: 8px;
    width: 1px;
    background: #a6a6a6;
}
.iframe-handle-container .tab-handle:before {
    left: 5px;
}
.iframe-handle-container .tab-handle:after {
    left: 8px;
}
.iframe-handle-container .tab-handle:hover {
    background: #178df7;
}
.iframe-handle-container .tab-handle:hover:before,
.iframe-handle-container .tab-handle:hover:after {
    background: #48a5f9;
}
.iframe-handle-container .gutter-handle {
    position: absolute;
    top: 0;
    left: -3px;
    width: 4px;
    height: 100%;
    cursor: col-resize;
    pointer-events: all;
}
.iframe-handle-container:hover .handle:before {
    display: block;
}
.iframe-handle-container:hover .tab-handle {
    background: #178df7;
    border-color: #178df7;
}
.iframe-handle-container:hover .tab-handle:before,
.iframe-handle-container:hover .tab-handle:after {
    background: #48a5f9;
}

.dim-indicator {
    position: absolute;
    top: 10%;
    height: 24px;
    margin-left: 22px;
    pointer-events: all;
    font-size: 14px;
    display: none;
    padding: 2px 9px;
    border-radius: 4px !important;
    color: #ffffff;
    /* font-weight: bold; */
    width: 156px;
    background-color: rgb(0, 0, 0);
}

.mobile-wrapper{
    position: absolute;
    top: 15%;
    margin-left: -30px;
    pointer-events: all;
    color: #ffffff;
    width: 340px;
    padding: 14px;
}

.mobile-list li{
    background: #808080;
    padding: 3px;
    border-radius: 4px !important;
    margin-bottom: 5px;
    list-style-type: none;
    padding-left: 11px;
}
  1. When the user clicks on a device screen icon I'm showing the handler to resize the canvas. --- To do that I created a method called "initDeviceEventHandle" that is in charge of initialising the gutter handler and another method called "showDeviceResolution" that create a clone copy of the two DIV that we explained above.
 editor.Commands.add('set-device-tablet', {
                run: function(editor, sender)
                {
                 .....
                },
                stop: function stop(editor, sender) {
                  
                    var device = deviceManager.get('Tablet High Resolution');               
                    showDeviceResolution("tablet_portrait", device);
                    initDeviceEventHandle(device);

                }
            });

/**
     * This function will receive a screen type and it will prompt the description on the left side of the canvas.
     *
     * @param type
     * @param device
     */
    var showDeviceResolution = function (type, device){
        var name, description;
        var width = device.attributes.widthMedia;

        // To reduce a little bit the width so we can move the handle until this size
        var reduceWidth = width - maxReduceScreenPreviewSize;
        $(".gjs-frame").width(reduceWidth);

        switch (type) {
            case "mobile_portrait":
                name = "Phone - Portrait";
                description = "Affects - " + width  + " and below";

                break;
            case "mobile_landscape":
                name = "Phone - Landscape";
                description = "Affects - " + width +" and below";

                break;
            case "tablet_portrait":
                name = "Tablet - Portrait";
                description = "Affects - " + width +" and below";


        }


        const deviceResolution = $(document).find(".gjs-cv-canvas").find(".device-resolution");

        if (deviceResolution.length > 0){
            deviceResolution.addClass("hidden");
            deviceResolution.find(".dr-type").html(name);
            deviceResolution.find(".dr-measure").html(description);



        } else {

            var copyDeviceResolution =  $(document).find(".device-resolution").clone(true,true);
            copyDeviceResolution.find(".dr-type").html(name);
            copyDeviceResolution.find(".dr-measure").html(description);
            $(document).find(".gjs-cv-canvas").prepend(copyDeviceResolution);


        }

        const glutterResize = $(document).find(".gjs-cv-canvas").find(".iframe-handle-container");

        if (glutterResize.length > 0){
            glutterResize.addClass("hidden");


        } else {
           // If the div is not created yet inside the iframe then we include it.
            var copyGlutterResize =  $(document).find(".iframe-handle-container").clone(true,true);
            $(".gjs-frame").before(copyGlutterResize);


        }

        // We force to refresh the screen because then we will update all dimensions
        setTimeout(function () {
            $(window).trigger('resize');
        }, 600);

    };


 /**
     * This function initializes device glutter handle
     *
     */
    var initDeviceEventHandle = function (device) {

        try {
              
                var maxDeviceSize =  parseInt(device.attributes.widthMedia,10);

                // To reduce a little bit the width so we can move the handle until this size
                var reduceWidth = maxDeviceSize - maxReduceScreenPreviewSize;
                $(".gjs-frame").width(reduceWidth);

                var widthIframe = 0; // Current iframe Width
                var maxLeftPos = 0;
                var finalwidth   = 0;

                $(".right-handle").draggable({
                    axis: "x",
                    start: function(event, ui) {
                        widthIframe = $(".gjs-frame").width();

                    },
                    drag: function(event, ui) {
                      
                        try {

                            if ( $(".gjs-cv-canvas").find(".handle-mask").length == 0 ){
                               // We need to create a mask to avoid in the moment that we are dragging to move the pointer over the iframe and losing then the control of the resizing.
                                $(".gjs-cv-canvas").append('<div class="handle-mask" style="position: absolute; z-index: 2; left: 0; top: 0; right: 0; bottom: 0;"></div>');
                            }

                            // We need to change the iframe width dynamically
                            const total = ui.position.left - ui.originalPosition.left;
                            var width = widthIframe + 2*total;
                            
                            $('.js-mobile-list').find(".mobile-item").addClass("hidden");
 
                            if(width > maxDeviceSize || width < minReduceScreenPreviewSize){
                                ui.position.left = maxLeftPos;

                            } else {
                                // Set the iframe width
                                maxLeftPos = ui.position.left;
                                $(".gjs-frame").width(width);
                                finalwidth = width;

                                // Set the position left of the left handle
                                var left =  $(".gjs-frame")[0].offsetLeft;
                                $(".left-handle").css("left", left);
                                
                                var leftDesc = left - 162; // 162 = the right panel width
                                $(".device-resolution").css("left", leftDesc);
                                $('.dim-indicator').html('Screen size  ' + width + 'px').fadeIn('slow');

                            }
                           // After dragging we need to refresh the editor to re-calculate the highlight border in the element selected.
                            editor.refresh();

                        } catch (err) {
                            console.error(err);
                        }
                    },
                    stop: function(event, ui) {
                      
                        try {
                            $(".handle-mask").remove();
                            
                        } catch (err) {
                            console.error(err);
                        }
                    }

                });



        } catch (err) {
            console.error(err);
        }
    };
  1. This point is important to do it because when we are resizing the screen then we need also to control as well the gutter. So in the editor "load" method, I'm including a new function to check if the user is resizing the canvas or the screen size. This is called "getWindowDims".
editor.on('load', function() {
// Control that the screen size is not too small
           getWindowDims();
}

/**
    * Function to determine Viewport Size
    */
   var getWindowDims = function() {
       var doc = document, w = window;
       var docEl = (doc.compatMode && doc.compatMode === 'CSS1Compat')?
           doc.documentElement: doc.body;

       var width = docEl.clientWidth;
       var height = docEl.clientHeight;

       // mobile zoomed in?
       if ( w.innerWidth && width > w.innerWidth ) {
           width = w.innerWidth;
           height = w.innerHeight;
       }

       // IMPORTANT!!!!
       // Glutter Handle information
       var glutterHandleObj = $(document).find(".gjs-cv-canvas").find(".iframe-handle-container");
       if (glutterHandleObj.length > 0){

           var leftGlutterHandleBar = glutterHandleObj.find(".left-handle");
           var rightGlutterHandleBar = glutterHandleObj.find(".right-handle");

           var leftOffset = $('.gjs-frame')[0].offsetLeft;
           var rightOffset =  $('.gjs-frame')[0].offsetLeft + $('.gjs-frame').width();

           leftGlutterHandleBar.css("cssText", "left: " + leftOffset  + "px !important");
           rightGlutterHandleBar.css("cssText", "left: " + rightOffset  + "px !important");
           glutterHandleObj.removeClass("hidden");

       }

       return {width: width, height: height};
   };

I hope this will be useful. Don't hesite to ask me any doubts. Demo link: http://recordit.co/RTEaUphDZ6

artfAugust 21, 20180 reactions

No @shlomoko, canvas is not resizable but I'd be glad to see a PR for that (possibly with the same logic used in panels)

jvillenaOctober 24, 20180 reactions

Hi @artf, I've just implemented a resizing canvas bar but I don't know if exist a way to refresh the position of the highlight borders. Take a look in the video please: http://recordit.co/jKlppYg2dD gjs-badge gjs-toolbar Best regards.

Related Questions and Answers

Continue research with similar issue discussions.

Paid Plugins That Match This Issue

Curated by issue keywords and label relevance to help you ship faster.

View all plugins

Loading paid plugin recommendations...

Browse Plugin Categories

Jump directly to plugin category pages on the marketplace.