Hacking Magento: Creating an HTML5 Canvas Customizer on Magento Bundles
Phillip Jackson @philwinkle Lead Magento Architect Something Digital
NEW
“Hacking” Magento
Hacker: 1. who makes innovative customizations or combinations of computer equipment � 2. who combines excellence, playfulness, cleverness and exploration in performed activities
The Client
The Client • Custom Printing • Upscale Products • Wedding, Bar/Bat Mitzvah, Holiday • Seasonal Business
The Requirements
The Requirements: Feature List RTL Language Physical Elements Tier Price by Element Drag and Drop Photo Upload Skip Steps Content Filtering SVG Clip Global Color Change Font Library Login AJAX Special Characters SVG Motif Library RWD Scale Canvas Hebrew Text Panel Project Save Font Pick List Arrange Movement Edit from Admin Spot Colors Undo / Redo Edit from Cart CMYK Conversion Retina Support Constrain Movement PDF Output Tooltips / Walkthrough Blocking Zone IE9 Support Event Wizard Global Font Change Realtime Price Estimate iPad Support
The Requirements: Feature List RTL Language Physical Elements Tier Price by Element Drag and Drop Photo Upload Skip Steps Content Filtering SVG Clip Global Color Change Font Library Login AJAX Special Characters SVG Motif Library RWD Scale Canvas Hebrew Text Panel Project Save Font Pick List Arrange Movement Edit from Admin Spot Colors Undo / Redo Edit from Cart CMYK Conversion Retina Support Constrain Movement PDF Output Tooltips / Walkthrough Blocking Zone IE9 Support Event Wizard Global Font Change Realtime Price Estimate iPad Support
Why Bundles?
Why Bundles? • Tier Pricing Support • Flexibility Across Product Models • Default Quantities and Selections • Additive/Subtractive Pricing Model
Additive Pricing Model $5.50 Feather $0.75 $7.60 Beautiful Ribbon $1.00 Product Add’l Color $0.35
Subtractive Pricing Model $5.50 Feather $0.75 $3.40 Beautiful Ribbon $1.00 Product Layer $0.35
Why Bundles?
Why Bundles? Bundle Simple Simple Simple Simple Related Related Related Related Related Related Related Related Related Related Related Related
Why Canvas?
Why Canvas? • Library Quality • Library Support • Hardware Acceleration • Render Time Benchmarks • Export to PNG (previews)
HTML5 Canvas
SVG
Raphael vs Fabric.js Raphael Fabric.js API Good Great Purpose Charting Canvas Support Good Great Active Development Poor Excellent Resources / Learning Poor Great Community Poor Great
Raphael
Fabric.js
Why Backbone?
Why Backbone • Shared Knowledge • Previous Experience • Candidate Pool • Tooling • Ember, Angular were too new
The Build
Delegation
The Build: Delagation pdf gen Jon menus events layers logic bundles photos Phillip undo pricing edit admin Daniel rtl clip frontend api Anna
Resources
The Build: Resources
Waterfall
The Build: Waterfall • 2-week sprints • Fixed cost • Tight deployment window
Tech Stack
The Build: Tech Stack
The Code
The Code • Prototype.js is not AMD compatible • Prototype is necessary for bundle.js, product.js, validation • jQuery Promises • Underscore.js gives us ES6 map, apply, FP
MV-Whatever
The Code: MV-Whatever • Global Application for defining regions • Models and views are obvious • “Controllers” are basically logic centers • We have 6 distinct controllers • Single router for the app
The Code: MV-Whatever Application var Customizer = new Marionette.Application(); � Customizer.on('start', function (options){ � Customizer.addRegions({ lightbox : LightBox, // can be a region object dialog : '#dialogContainer' // or it can be a dom node }); � }); � //------------------------------- // FIRE UP YOUR ENGINES //------------------------------- Customizer.start();
The Code: MV-Whatever A typical controller var AdminController = Backbone.Marionette.Controller.extend({ initialize: function (){ //create new project model appRepository.project.set({ 'url' : window._adminSaveUrl, 'params' : window._adminUrlParams, 'adminMode' : true }); � //replace default saveProject action on the customizerController customizerController.saveProject = function (){ appRepository.project.saveData(); } } }); � return new AdminController();
Dependency Management
The Code: Dependency Management • Git submodules • NPM / Node • Require.js
The Code: Dependency Management Good ;define ([ 'app', 'jquery', 'backbone', 'collections/canvas/sides', 'collections/interface/envelope-styles', 'fabric', 'logic/fontcontroller' , 'models/apprepository', 'models/customizer-repository' ], function ( Customizer, $, Backbone, stepsCollection, envStylesCollection, Fabric, fontController, appRepository, customizerRepository ){
The Code: Dependency Management Better ;define (['app'], function (Customizer){ � var InvitationController = Backbone.Marionette.Controller.extend({ � getSides: { require(['collections/canvas/sides'], function (sides){ //.. }); }, getStyles: { require(['collections/interface/envelope-styles'], function (styles){ //.. }); }
Sensible Structure
The Code: Sensible Structure 📂 js/lib/customizer 📂 collections 📂 plugins 📂 logic 📂 models 📂 templates 📂 views
The Code: Sensible Structure 📂 js/lib/customizer/collections 📂 canvas 📅 designs.js 📅 sides.js 📂 cart 📂 interface
The Challenges
The Challenges • Multi-bundles • Tier pricing • Async • Responsive-ish • No round-tripping
bundle.js
The Challenges: bundle.js options
The Challenges: bundle.js selections
The Challenges: bundle.js var colorSelections = _.chain(bundle.config.options) .map( function (a) { if (a.title=='Ink Color Count'){ return _.values(a.selections); } }) .filter() .map( function (b) { var tmp = []; _.each(b, function (c){ if (c.name.charAt(0)==colorCount){ tmp.push(c); } }); //workaround because we can't return from the //`each` so we fetch it out of the array return tmp [0] ; },context) .value();
The Challenges: bundle.js Group Simple (fixed) Bundle Group Simple (tier) Simple Simple Simple
The Challenges: bundle.js
The Demo
Thank You! @philwinkle @somethingdigitl @magetalk
Recommend
More recommend