@behance/helicropter
Advanced tools
Comparing version 15.3.0 to 15.4.0
{ | ||
"name": "@behance/helicropter", | ||
"version": "15.3.0", | ||
"version": "15.4.0", | ||
"description": "My helicropter goes \"whosh whosh whosh\"", | ||
@@ -5,0 +5,0 @@ "main": "src/js/index.js", |
@@ -23,6 +23,13 @@ import View from '@behance/beff/View'; | ||
const canvasContainerClass = 'js-canvas-container'; | ||
this._canvas = new fabric.Canvas(this._$canvas[0], { | ||
selection: false, | ||
containerClass: canvasContainerClass, | ||
}); | ||
this.$canvasContainer = this.$view.find(`.${canvasContainerClass}`); | ||
this.$cropperCanvas = this.$view.find('.js-cropper-canvas'); | ||
this.$canvasContainer.css('transform-origin', 'left top'); | ||
// IMPORTANT: Using undocumented Fabric.js API here to force retina-like behavior | ||
@@ -67,2 +74,6 @@ // even on screens that are not retina. This is a hack to mitigate the lack of anti-aliasing | ||
'scale-view'({ scale }) { | ||
this._scaleView({ scale }); | ||
}, | ||
'set-image'(imageSrc, coordinates) { | ||
@@ -72,2 +83,4 @@ this._model.image = imageSrc; | ||
.catch((err) => console.error(err)); | ||
this.trigger('cropper-set-image'); | ||
}, | ||
@@ -164,2 +177,24 @@ | ||
_scaleView({ scale }) { | ||
this.$canvasContainer.css({ | ||
transform: `scale(${scale})`, | ||
}); | ||
requestAnimationFrame(() => { | ||
const boundRect = this.$cropperCanvas[0].getBoundingClientRect(); | ||
let width = boundRect.width; | ||
let height = boundRect.height; | ||
if (!boundRect.width || !boundRect.height) { | ||
width = parseInt(this.$cropperCanvas.width()) * scale; | ||
height = parseInt(this.$cropperCanvas.height()) * scale; | ||
} | ||
this.$canvasContainer.css({ | ||
width, | ||
height, | ||
}); | ||
}); | ||
}, | ||
_setCropSizeByAspectRatio(ratio) { | ||
@@ -166,0 +201,0 @@ const maxWidth = Math.round(this._model.canvasWidth - (MINIMUM_PADDING * 2)); |
@@ -15,2 +15,4 @@ import extend from '@behance/nbd/util/extend'; | ||
import $ from 'jquery'; | ||
const HelicropterView = View.extend({ | ||
@@ -74,2 +76,4 @@ mustache, | ||
this._scale = 1; | ||
this._bindSubsections(); | ||
@@ -182,4 +186,37 @@ }, | ||
_initScaleView() { | ||
if (!this._model.get('resize')) { | ||
return; | ||
} | ||
this._resize = Object.assign({ | ||
boundEl: document, | ||
minHeight: 0, | ||
offset: 30, | ||
}, this._model.get('resize')); | ||
const $boundEl = $(this._resize.boundEl); | ||
const initialHeight = $boundEl.outerHeight(true); | ||
this.bar = initialHeight + this._resize.offset; | ||
window.addEventListener('resize', () => this._calculateViewScale()); | ||
this._calculateViewScale(); | ||
}, | ||
_calculateViewScale() { | ||
if (window.innerHeight > this.bar || window.innerHeight < this._resize.minHeight) { | ||
this.trigger('scale-out-of-bound'); | ||
return; | ||
} | ||
this._scale = Number(parseFloat(window.innerHeight / this.bar).toFixed(2)); | ||
this.trigger('scale-view', { scale: this._scale }); | ||
}, | ||
_bindSubsections() { | ||
this._croppingArea.relay(this._zoomSlider, 'scale'); | ||
this._croppingArea.relay(this, 'scale-view'); | ||
this._zoomSlider.relay(this._croppingArea, 'image-loaded set-crop-size'); | ||
@@ -196,2 +233,6 @@ | ||
}, | ||
'cropper-set-image'() { | ||
this._initScaleView(); | ||
}, | ||
}); | ||
@@ -287,2 +328,7 @@ | ||
}, | ||
destroy() { | ||
window.removeEventListener('resize', () => this._calculateViewScale()); | ||
this._super(); | ||
}, | ||
}); | ||
@@ -329,2 +375,3 @@ | ||
const { dimensions, src } = this.crop(); | ||
const scale = this._view._scale; | ||
@@ -335,4 +382,4 @@ return BeffImage.load(src).then(beffImage => { | ||
canvas.width = width; | ||
canvas.height = height; | ||
canvas.width = width * scale; | ||
canvas.height = height * scale; | ||
ctx.drawImage( | ||
@@ -346,4 +393,4 @@ beffImage.image, | ||
0, | ||
width, | ||
height, | ||
width * scale, | ||
height * scale, | ||
); | ||
@@ -350,0 +397,0 @@ |
@@ -13,2 +13,6 @@ import CroppingArea from 'CroppingArea'; | ||
this.croppingArea = createCroppingArea(this.$el); | ||
this.waitAFrame = function() { | ||
return new Promise(resolve => requestAnimationFrame(() => resolve())); | ||
}; | ||
}); | ||
@@ -56,2 +60,15 @@ | ||
it('scales view to the value', function(done) { | ||
const width = this.croppingArea.$canvasContainer.width(); | ||
const height = this.croppingArea.$canvasContainer.height(); | ||
const scale = .5; | ||
this.croppingArea._scaleView({ scale }); | ||
this.waitAFrame().then(() => { | ||
expect(this.croppingArea.$canvasContainer.width()).toEqual(width * scale); | ||
expect(this.croppingArea.$canvasContainer.height()).toEqual(height * scale); | ||
done(); | ||
}); | ||
}); | ||
describe('#getCropData', function() { | ||
@@ -58,0 +75,0 @@ it('returns undefined if no image is defined', function() { |
@@ -351,2 +351,101 @@ import $ from 'jquery'; | ||
}); | ||
describe('when a user resizes the browser', function() { | ||
beforeEach(function() { | ||
this.initialWindowHeight = 600; | ||
window.innerHeight = this.initialWindowHeight; | ||
this.waitAFrame = function() { | ||
return new Promise(resolve => requestAnimationFrame(() => resolve())); | ||
}; | ||
this.resizeWindowHeightTo = (value) => { | ||
window.innerHeight = value; | ||
window.dispatchEvent(new Event('resize')); | ||
}; | ||
}); | ||
it('resizes cropper to the scaled value', function(done) { | ||
this.helicropter = this._createWithoutInitialImage({ | ||
resize: { | ||
offset: 0, | ||
}, | ||
}); | ||
this.helicropter.render($('#jasmine-fixtures')); | ||
this.helicropter._view._croppingArea.trigger('cropper-set-image'); | ||
const $cropper = this.helicropter._view._croppingArea.$canvasContainer; | ||
const oldHeight = $cropper.height(); | ||
const scale = .5; | ||
const expectedHeight = oldHeight * scale; | ||
this.helicropter._view.on('scale-view', () => { | ||
this.waitAFrame().then(() => { | ||
const height = $cropper.height(); | ||
expect(height).toEqual(expectedHeight); | ||
done(); | ||
}); | ||
}); | ||
this.resizeWindowHeightTo(this.initialWindowHeight * scale); | ||
}); | ||
it('starts resizing when window height is reaching edges of boundEl height', function(done) { | ||
$('#jasmine-fixtures').append('<div class="bound"></div>'); | ||
const boundElHeight = window.innerHeight / 2; | ||
$('.bound').height(boundElHeight); | ||
this.helicropter = this._createWithoutInitialImage({ | ||
resize: { | ||
boundEl: $('.bound')[0], | ||
offset: 0, | ||
}, | ||
}); | ||
this.helicropter._view._croppingArea.trigger('cropper-set-image'); | ||
this.helicropter._view.on('scale-view', done); | ||
this.resizeWindowHeightTo(boundElHeight - 1); | ||
}); | ||
it('stops scaling the cropper when :minHeight is reached', function(done) { | ||
const minHeight = window.innerHeight * .4; | ||
this.helicropter = this._createWithoutInitialImage({ | ||
resize: { | ||
minHeight, | ||
}, | ||
}); | ||
this.helicropter.render($('#jasmine-fixtures')); | ||
this.helicropter._view._croppingArea.trigger('cropper-set-image'); | ||
const $cropper = this.helicropter._view._croppingArea.$canvasContainer; | ||
let oldHeight; | ||
this.helicropter._view.on('scale-out-of-bound', () => { | ||
this.waitAFrame().then(() => { | ||
const height = $cropper.height(); | ||
expect(height).toEqual(oldHeight); | ||
done(); | ||
}); | ||
}); | ||
this.helicropter._view.on('scale-view', () => { | ||
this.waitAFrame().then(() => { | ||
oldHeight = $cropper.height(); | ||
this.resizeWindowHeightTo(minHeight - 1); | ||
}); | ||
}); | ||
this.resizeWindowHeightTo(minHeight + 1); | ||
}); | ||
}); | ||
}); |
8677858
4415