Wip on dropper and gravity editor directives

This commit is contained in:
Per Ploug Krogslund
2014-01-30 22:28:50 +01:00
parent c52123e878
commit 7d7fcd2093
14 changed files with 219 additions and 214 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -5,21 +5,20 @@
* @function
**/
angular.module("umbraco.directives")
.directive('umbImageCrop', function ($timeout, localizationService, $log) {
.directive('umbImageCrop',
function ($timeout, localizationService, cropperHelper, $log) {
return {
restrict: 'E',
replace: true,
templateUrl: 'views/directives/imaging/umb-image-crop.html',
scope: {
src: '=',
width: '=',
height: '=',
width: '@',
height: '@',
crop: "="
},
link: function(scope, element, attrs) {
scope.scale = 100;
//if image is over this, we re-calculate the editors global ratio
//this will not have an effect on the result, since that is returned in percentage
scope.maxHeight = 500;
@@ -33,15 +32,19 @@ angular.module("umbraco.directives")
cropper:{},
viewport:{},
margin: 20,
ratio: 1
scale: {
min: 0.3,
max: 3,
current: 1
}
};
scope.style = function () {
return {
'height': (parseInt(scope.height, 10) + 2 * scope.dimensions.margin) + 'px',
'width': (parseInt(scope.width, 10) + 2 * scope.dimensions.margin) + 'px'
};
};
return {
'height': (parseInt(scope.height, 10) + 2 * scope.dimensions.margin) + 'px',
'width': (parseInt(scope.width, 10) + 2 * scope.dimensions.margin) + 'px'
};
};
//elements
@@ -52,10 +55,17 @@ angular.module("umbraco.directives")
//default constraints for drag n drop
var constraints = {left: {max: 20, min: 20}, top: {max: 20, min: 20}, };
var setDimensions = function(){
scope.dimensions.image.width = $image.width();
scope.dimensions.image.height = $image.height();
var setDimensions = function(originalImage){
originalImage.width("auto");
originalImage.height("auto");
scope.dimensions.image.originalWidth = originalImage.width();
scope.dimensions.image.originalHeight = originalImage.height();
scope.dimensions.image.width = originalImage.width();
scope.dimensions.image.height = originalImage.height();
scope.dimensions.image.left = originalImage[0].offsetLeft;
scope.dimensions.image.top = originalImage[0].offsetTop;
scope.dimensions.viewport.width = $viewport.width();
scope.dimensions.viewport.height = $viewport.height();
@@ -67,36 +77,28 @@ angular.module("umbraco.directives")
var setImageSize = function(width, height){
$image.width(width);
$image.height(height);
scope.dimensions.image.width = width;
scope.dimensions.image.height = height;
scope.dimensions.image.left = $image[0].offsetLeft;
scope.dimensions.image.top = $image[0].offsetTop;
};
//when loading an image without any crop info, we center and fit it
var fitImage = function(){
fitToViewPort($image);
centerImage($image);
cropperHelper.centerInsideViewPort($image, $viewport);
syncOverLay();
setConstraints($image);
};
//utill for centering scaled image
var centerImage = function(img) {
var image_width = img.width(),
image_height = img.height(),
mask_width = $viewport.width(),
mask_height = $viewport.height();
img.css({
'position': 'absolute',
'left': scope.dimensions.viewport.width / 2 - scope.dimensions.image.width / 2,
'top': scope.dimensions.viewport.height / 2 - scope.dimensions.image.height / 2
});
};
//utill for scaling image to fit viewport
var fitToViewPort = function(img) {
//returns size fitting the cropper
var size = calculateAspectRatioFit(
var size = cropperHelper.calculateAspectRatioFit(
scope.dimensions.image.width,
scope.dimensions.image.height,
scope.dimensions.cropper.width,
@@ -106,20 +108,16 @@ angular.module("umbraco.directives")
//sets the image size and updates the scope
setImageSize(size.width, size.height);
scope.minScale = size.ratio;
scope.maxScale = size.ratio * 3;
scope.currentScale = scope.minScale;
scope.scale = scope.currentScale;
scope.dimensions.scale.min = size.ratio;
scope.dimensions.scale.max = size.ratio * 3;
scope.dimensions.scale.current = size.ratio;
};
var resizeImageToScale = function(img, ratio){
//do stuff
var size = calculateSizeToRatio(scope.dimensions.image.originalWidth, scope.dimensions.image.originalHeight, ratio);
var size = cropperHelper.calculateSizeToRatio(scope.dimensions.image.originalWidth, scope.dimensions.image.originalHeight, ratio);
setImageSize(size.width, size.height);
centerImage(img);
scope.currentScale = scope.scale;
syncOverLay();
};
@@ -135,23 +133,7 @@ angular.module("umbraco.directives")
constraints.top.min = 20 + crop_height - h;
};
//utill for getting either min/max aspect ratio to scale image after
var calculateAspectRatioFit = function(srcWidth, srcHeight, maxWidth, maxHeight, maximize) {
var ratio = [maxWidth / srcWidth, maxHeight / srcHeight ];
if(maximize){
ratio = Math.max(ratio[0], ratio[1]);
}else{
ratio = Math.min(ratio[0], ratio[1]);
}
return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio};
};
//utill for scaling width / height given a ratio
var calculateSizeToRatio= function(srcWidth, srcHeight, ratio) {
return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio};
};
var calculateCropBox = function(){
scope.crop.left = Math.abs($image[0].offsetLeft - scope.dimensions.margin) / scope.dimensions.image.width;
@@ -169,9 +151,20 @@ angular.module("umbraco.directives")
var cropped_width = scope.dimensions.image.originalWidth - left;
var ratio = cropped_width / scope.dimensions.image.originalWidth;
scope.scale = ratio;
resizeImageToScale($image, ratio);
var original = cropperHelper.calculateAspectRatioFit(
scope.dimensions.image.originalWidth,
scope.dimensions.image.originalHeight,
scope.dimensions.cropper.width,
scope.dimensions.cropper.height,
true);
scope.dimensions.scale.current = ratio;
//min max based on original width/height
scope.dimensions.scale.min = original.ratio;
scope.dimensions.scale.max = 2;
resizeImageToScale($image, ratio);
$image.css({
"top": -top,
"left": -left
@@ -181,7 +174,6 @@ angular.module("umbraco.directives")
};
var syncOverLay = function(){
$overlay.height($image.height());
$overlay.width($image.width());
@@ -194,6 +186,8 @@ angular.module("umbraco.directives")
calculateCropBox();
};
//Drag and drop positioning, using jquery ui draggable
var onStartDragPosition, top, left;
$overlay.draggable({
@@ -201,8 +195,6 @@ angular.module("umbraco.directives")
syncOverLay();
},
drag: function(event, ui) {
if(ui.position.left <= constraints.left.max && ui.position.left >= constraints.left.min){
$image.css({
'left': ui.position.left
@@ -215,50 +207,59 @@ angular.module("umbraco.directives")
});
}
},
stop: function() {
stop: function(event, ui) {
scope.dimensions.image.left = $image[0].offsetLeft;
scope.dimensions.image.top = $image[0].offsetTop;
syncOverLay();
}
});
var init = function(image){
scope.loaded = false;
//set dimensions on image, viewport, cropper etc
setDimensions(image);
//if we have a crop already position the image
if(scope.crop && scope.crop.top){
calculatePosition(scope.crop);
}else{
//if not, reset it and fit the image to the viewport
scope.crop = {};
fitImage();
}
scope.loaded = true;
};
//// INIT /////
$image.load(function(){
$timeout(function(){
$image.width("auto");
$image.height("auto");
scope.dimensions.image.originalWidth = $image.width();
scope.dimensions.image.originalHeight = $image.height();
setDimensions();
if(scope.crop && scope.crop.top){
calculatePosition(scope.crop);
}else{
scope.crop = {};
fitImage();
}
scope.loaded = true;
init($image);
});
});
/// WATCHERS ////
scope.$watch("scale", function(){
if(scope.loaded && scope.scale !== scope.currentScale){
resizeImageToScale($image, scope.scale);
setConstraints($image);
}
scope.$watchCollection('[width, height]', function(newValues, oldValues){
//we have to reinit the whole thing if
//one of the external params changes
if(newValues !== oldValues){
setDimensions($image);
}
});
/// WATCHERS ////
scope.$watch("crop", function(newVal, oldVal){
if(scope.loaded && newVal !== oldVal){
calculatePosition(scope.crop);
scope.$watch("dimensions.scale.current", function(){
if(scope.loaded){
resizeImageToScale($image, scope.dimensions.scale.current);
setConstraints($image);
}
});
}

View File

@@ -20,98 +20,57 @@ angular.module("umbraco.directives")
},
link: function(scope, element, attrs) {
scope.dimensions = {
width: 0,
height: 0,
left: 0,
top: 0
};
//elements
var $viewport = element.find(".viewport");
var $image = element.find("img");
var $overlay = element.find(".overlay");
scope.style = function () {
return {
'top': scope.dimensions.top + 'px',
'left': scope.dimensions.left + 'px'
};
};
var setDimensions = function(){
scope.imagewidth = $image.width();
scope.imageheight = $image.height();
};
scope.dimensions.width = $image.width();
scope.dimensions.height = $image.height();
var setImageSize = function(width, height){
$image.width(width);
$image.height(height);
$viewport.width(width);
$viewport.height(height);
};
var fitImage = function(){
fitToViewPort($image);
centerImage($image);
$log.log("centered and fitted");
};
//utill for centering scaled image
var centerImage = function(img) {
img.css({
'position': 'absolute',
'left': scope.width / 2 - scope.imageWidth / 2,
'top': scope.height / 2 - scope.imageHeight / 2
});
};
//utill for scaling image to fit viewport
var fitToViewPort = function(img) {
//returns size fitting the cropper
var size = calculateAspectRatioFit(
scope.imageWidth,
scope.imageHeight,
scope.width,
scope.height,
false);
//sets the image size and updates the scope
setImageSize(size.width, size.height);
};
//utill for getting either min/max aspect ratio to scale image after
var calculateAspectRatioFit = function(srcWidth, srcHeight, maxWidth, maxHeight, maximize) {
var ratio = [maxWidth / srcWidth, maxHeight / srcHeight ];
if(maximize){
ratio = Math.max(ratio[0], ratio[1]);
}else{
ratio = Math.min(ratio[0], ratio[1]);
if(scope.gravity){
scope.dimensions.left = scope.gravity.left * scope.dimensions.width -10;
scope.dimensions.top = scope.gravity.top * scope.dimensions.height -10;
}
return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio};
};
var calculateGravity = function(){
scope.gravity.left = $overlay[0].offsetLeft + 10;
scope.gravity.top = $overlay[0].offsetTop + 10;
scope.dimensions.left = $overlay[0].offsetLeft + 10;
scope.dimensions.top = $overlay[0].offsetTop + 10;
scope.gravity.left = scope.gravity.left / scope.dimensions.width;
scope.gravity.top = scope.gravity.top / scope.dimensions.height;
};
//Drag and drop positioning, using jquery ui draggable
var onStartDragPosition, top, left;
$overlay.draggable({
stop: function() {
calculateGravity();
}
});
//// INIT /////
$image.load(function(){
$timeout(function(){
$image.width("auto");
$image.height("auto");
setDimensions();
fitImage();
scope.loaded = true;
});
});
}
};
});

View File

@@ -7,7 +7,8 @@
* Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form.
**/
angular.module("umbraco.directives")
.directive('umbImageThumbnail', function ($timeout, localizationService, $log) {
.directive('umbImageThumbnail',
function ($timeout, localizationService, cropperHelper, $log) {
return {
restrict: 'E',
replace: true,

View File

@@ -19,7 +19,7 @@ function mediaHelper(umbRequestHelper) {
* @param {object} options.imageOnly Optional, if true then will only return a path if the media item is an image
*/
getMediaPropertyValue: function (options) {
return "assets/img/mocks/image.jpg";
return "assets/img/mocks/big-image.jpg";
},
/**
@@ -35,7 +35,7 @@ function mediaHelper(umbRequestHelper) {
* @param {object} options.imageModel The media object to retrieve the image path from
*/
getImagePropertyValue: function (options) {
return "assets/img/mocks/image.jpg";
return "assets/img/mocks/big-image.jpg";
},
/**
* @ngdoc function

View File

@@ -1,7 +1,7 @@
var umbracoAppDev = angular.module('umbraco.httpbackend', ['umbraco', 'ngMockE2E', 'umbraco.mocks']);
function initBackEnd($httpBackend, contentMocks, mediaMocks, treeMocks, userMocks, contentTypeMocks, sectionMocks, entityMocks, dataTypeMocks, dashboardMocks, macroMocks, utilMocks, localizationMocks, prevaluesMocks, imageHelperMocks) {
function initBackEnd($httpBackend, contentMocks, mediaMocks, treeMocks, userMocks, contentTypeMocks, sectionMocks, entityMocks, dataTypeMocks, dashboardMocks, macroMocks, utilMocks, localizationMocks, prevaluesMocks) {
console.log("httpBackend inited");
@@ -18,8 +18,6 @@ function initBackEnd($httpBackend, contentMocks, mediaMocks, treeMocks, userMock
utilMocks.register();
localizationMocks.register();
prevaluesMocks.register();
imageHelperMocks.register();
entityMocks.register();
$httpBackend.whenGET(/^views\//).passThrough();

View File

@@ -0,0 +1,62 @@
/**
* @ngdoc service
* @name umbraco.services.mediaHelper
* @description A helper object used for dealing with media items
**/
function cropperHelper(umbRequestHelper) {
var service = {
//utill for getting either min/max aspect ratio to scale image after
calculateAspectRatioFit : function(srcWidth, srcHeight, maxWidth, maxHeight, maximize) {
var ratio = [maxWidth / srcWidth, maxHeight / srcHeight ];
if(maximize){
ratio = Math.max(ratio[0], ratio[1]);
}else{
ratio = Math.min(ratio[0], ratio[1]);
}
return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio};
},
//utill for scaling width / height given a ratio
calculateSizeToRatio : function(srcWidth, srcHeight, ratio) {
return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio};
},
centerInsideViewPort : function(img, viewport){
var image_width = img.width(),
image_height = img.height(),
mask_width = viewport.width(),
mask_height = viewport.height(),
image_top = img[0].offsetTop,
image_left = $image[0].offsetLeft,
change = true;
var left = mask_width / 2 - image_width / 2,
top = mask_height / 2 - image_height / 2;
/*check for overflow
if(image_left > 20 || image_left < mask_width - image_width){
left = mask_width / 2 - image_width / 2;
change = true;
}*/
if(change){
img.css({
'position': 'absolute',
'left': left,
'top': top
});
}
}
};
return service;
}
angular.module('umbraco.services').factory('cropperHelper', cropperHelper);

View File

@@ -1,6 +1,6 @@
/** Executed when the application starts, binds to events and set global state */
app.run(['userService', '$log', '$rootScope', '$location', 'navigationService', 'appState', 'editorState', 'fileManager', 'assetsService', 'eventsService', '$cookies',
function (userService, $log, $rootScope, $location, navigationService, appState, editorState, fileManager, assetsService, eventsService, $cookies) {
app.run(['userService', '$log', '$rootScope', '$location', 'navigationService', 'appState', 'editorState', 'fileManager', 'assetsService', 'eventsService', '$cookies', '$templateCache',
function (userService, $log, $rootScope, $location, navigationService, appState, editorState, fileManager, assetsService, eventsService, $cookies, $templateCache) {
//This sets the default jquery ajax headers to include our csrf token, we

View File

@@ -4,13 +4,8 @@ yepnope({
'lib/jquery/jquery-2.0.3.min.js',
/* the jquery ui elements we need */
/* NOTE: I've opted not to use the full lib, just the parts we need to save on DL*/
'lib/jquery/jquery.ui.core.min.js',
'lib/jquery/jquery.ui.widget.min.js',
'lib/jquery/jquery.ui.mouse.min.js',
'lib/jquery/jquery.ui.sortable.min.js',
'lib/jquery/jquery-ui-1.10.3.custom.min.js',
/* 1.1.5 */
'lib/angular/1.1.5/angular.min.js',
'lib/angular/1.1.5/angular-cookies.min.js',

View File

@@ -8,25 +8,8 @@
<input
type="range"
min="{{minScale}}"
max="{{maxScale}}"
min="{{dimensions.scale.min}}"
max="{{dimensions.scale.max}}"
step="0.01"
ng-model="scale" />
<pre>
{{dimensions | json}}
</pre>
<pre>
{{box | json}}
</pre>
<!--
<ul class="crops-preview">
<li ng-repeat="preview in previews">
<div ng-style="preview.style">
<img src="{{preview.src" ng-style="preview.imageStyle">
</div>
</li>
</ul> -->
ng-model="dimensions.scale.current" />
</div>

View File

@@ -1,12 +1,16 @@
<div class="umb-cropper-gravity">
<div class="gravity-container">
<div class="viewport" ng-style="style()">
<img src="{{src}}" />
<div class="viewport">
<img src="{{src}}" style="max-width: 100%;" />
<div class="overlay">
<div class="overlay" ng-style="style()">
<i class="icon-crosshair"></i>
</div>
</div>
</div>
{{gravity | json }}
{{dimensions | json }}
</div>

View File

@@ -11,7 +11,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
//Data sample
var __img = {
//image to crop
src: "assets/img/mocks/image.jpg",
src: "assets/img/mocks/big-image.jpg",
//global gravity, used if not crop is specified
gravity: {left: 0.5, top: 0.4},
@@ -20,8 +20,8 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
thumbnail:
{
//crop dimensions
width: "30px",
height: "40px",
width: 100,
height: 100,
//crops in percentages
crop:{ "left": 0.31731772342645215,
@@ -33,8 +33,8 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
banner:
{
width: "200px",
height: "20px",
width: 340,
height: 90,
crop:{ "left": 0.31731772342645215,
"top": 0.17420325244997603,
@@ -45,8 +45,8 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
highrise:
{
width: "20px",
height: "200px"
width: 90,
height: 340
},
}
};
@@ -81,7 +81,6 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
$scope.edit = function(image){
$scope.currentImage = image;
$scope.cropper.image = __img;
}

View File

@@ -1,35 +1,38 @@
<div class="umb-editor umb-mediapicker" ng-controller="Umbraco.PropertyEditors.MediaPickerController">
<div ng-if="cropper.image">
<div ng-if="cropper.crop">
<umb-image-crop
crop="cropper.crop"
gravity="cropper.image.gravity"
<div ng-if="cropper.preset">
<umb-image-crop
height="{{cropper.preset.height}}"
width= "{{cropper.preset.width}}"
crop="cropper.preset.crop"
src="cropper.image.src" />
</div>
<div ng-if="cropper.grav">
<div ng-if="cropper.point">
<umb-image-gravity
height="'300px'"
width= "'480px'"
src="cropper.grav.src"
gravity="cropper.grav.gravity" />
height="200"
width= "380"
src="cropper.image.src"
gravity="cropper.point.gravity" />
</div>
<ul class="umb-sortable-thumbnails">
<li class="span2">
<img ng-src="{{cropper.image.src}}"
ng-click="cropper.crop = undefined; cropper.grav = cropper.image" />
ng-click="cropper.crop = undefined; cropper.point = cropper.image" />
original
</li>
<li ng-repeat="crop in cropper.image.crops">
<a href ng-click="cropper.crop = crop; cropper.grav = undefined">
<li ng-repeat="(key,value) in cropper.image.crops">
<a href ng-click="cropper.preset = value; cropper.point = undefined">
<umb-image-thumbnail
gravity="cropper.image.gravity"
crop="cropper.image.crop"
src="cropper.image.src"
height="crop.height"
width="crop.width">
height="value.height"
width="value.width">
</a>
</li>
@@ -38,7 +41,7 @@
<ul ui-sortable="sortableOptions" ng-model="images" class="umb-sortable-thumbnails">
<li style="width: 120px; height: 100px; overflow: hidden;" ng-repeat="image in images">
<img ng-src="{{image.thumbnail}}" alt="" ng-show="image.src">
<img ng-src="{{image.thumbnail}}" ng-click="edit(image)" alt="" ng-show="image.src">
<span class="icon-holder" ng-hide="image.src">
<i class="icon {{image.icon}} large" ></i>