494 lines
13 KiB
JavaScript
494 lines
13 KiB
JavaScript
$(function () {
|
|
|
|
'use strict';
|
|
|
|
var console = window.console || { log: function () {} };
|
|
var $image = $('#image');
|
|
var $download = $('#download');
|
|
var $dataX = $('#dataX');
|
|
var $dataY = $('#dataY');
|
|
var $dataHeight = $('#dataHeight');
|
|
var $dataWidth = $('#dataWidth');
|
|
var $dataRotate = $('#dataRotate');
|
|
var $dataScaleX = $('#dataScaleX');
|
|
var $dataScaleY = $('#dataScaleY');
|
|
var $dataBrush = $('#dataBrush');
|
|
var $dataBrushPoints = $('#dataBrushPoints');
|
|
var $dataBrushColor = $('#dataBrushColor');
|
|
var $dataBrushSize = $('#dataBrushSize');
|
|
var $brushStatus = $('#brushStatus');
|
|
var $imgContainer = $('.img-container');
|
|
var $brushCanvas = $('#brushCanvas');
|
|
var $brushSize = $('#brushSize');
|
|
var $brushSizeText = $('#brushSizeText');
|
|
var $brushColor = $('#brushColor');
|
|
var $toggleBrush = $('#toggleBrush');
|
|
var brushCanvas = $brushCanvas[0];
|
|
var brushCtx = brushCanvas ? brushCanvas.getContext('2d') : null;
|
|
var brushData = {
|
|
enabled: false,
|
|
size: 24,
|
|
color: '#ff0000',
|
|
points: []
|
|
};
|
|
var isDrawing = false;
|
|
var options = {
|
|
aspectRatio: NaN,
|
|
autoCropArea: 1,
|
|
preview: '.img-preview',
|
|
crop: function (e) {
|
|
$dataX.val(Math.round(e.x));
|
|
$dataY.val(Math.round(e.y));
|
|
$dataHeight.val(Math.round(e.height));
|
|
$dataWidth.val(Math.round(e.width));
|
|
$dataRotate.val(e.rotate);
|
|
$dataScaleX.val(e.scaleX);
|
|
$dataScaleY.val(e.scaleY);
|
|
}
|
|
};
|
|
|
|
function syncBrushData() {
|
|
if ($dataBrush.length) {
|
|
$dataBrush.val(JSON.stringify({
|
|
size: brushData.size,
|
|
color: brushData.color,
|
|
points: brushData.points
|
|
}));
|
|
}
|
|
if ($dataBrushPoints.length) {
|
|
$dataBrushPoints.val(encodeURIComponent(JSON.stringify(brushData.points)));
|
|
}
|
|
if ($dataBrushColor.length) {
|
|
$dataBrushColor.val(brushData.color);
|
|
}
|
|
if ($dataBrushSize.length) {
|
|
$dataBrushSize.val(brushData.size);
|
|
}
|
|
}
|
|
|
|
function getImageData() {
|
|
if (!$image.data('cropper')) {
|
|
return null;
|
|
}
|
|
return $image.cropper('getImageData');
|
|
}
|
|
|
|
function getDisplayRect() {
|
|
var imageData = getImageData();
|
|
var imgEl;
|
|
var imgRect;
|
|
var canvasRect;
|
|
if (!imageData || !brushCanvas) {
|
|
return null;
|
|
}
|
|
imgEl = $imgContainer.find('.cropper-canvas img').get(0);
|
|
if (!imgEl) {
|
|
return null;
|
|
}
|
|
imgRect = imgEl.getBoundingClientRect();
|
|
canvasRect = brushCanvas.getBoundingClientRect();
|
|
return {
|
|
left: imgRect.left - canvasRect.left,
|
|
top: imgRect.top - canvasRect.top,
|
|
width: imgRect.width,
|
|
height: imgRect.height,
|
|
naturalWidth: imageData.naturalWidth,
|
|
naturalHeight: imageData.naturalHeight
|
|
};
|
|
}
|
|
|
|
function ensureCanvasInCropper() {
|
|
var $cropperContainer = $imgContainer.find('.cropper-container');
|
|
if (!$cropperContainer.length || !$brushCanvas.length) {
|
|
return;
|
|
}
|
|
if (!$brushCanvas.parent().is($cropperContainer)) {
|
|
$brushCanvas.appendTo($cropperContainer);
|
|
$brushCanvas.css({
|
|
position: 'absolute',
|
|
left: 0,
|
|
top: 0,
|
|
'z-index': 20
|
|
});
|
|
}
|
|
}
|
|
|
|
function updateCanvasSize() {
|
|
var $cropperContainer = $imgContainer.find('.cropper-container');
|
|
if (!brushCanvas || !$imgContainer.length || !$cropperContainer.length) {
|
|
return;
|
|
}
|
|
var cropperWidth = Math.round($cropperContainer.innerWidth());
|
|
var cropperHeight = Math.round($cropperContainer.innerHeight());
|
|
brushCanvas.width = cropperWidth;
|
|
brushCanvas.height = cropperHeight;
|
|
$brushCanvas.css({
|
|
left: '0px',
|
|
top: '0px',
|
|
width: cropperWidth + 'px',
|
|
height: cropperHeight + 'px'
|
|
});
|
|
}
|
|
|
|
function renderBrushOverlay() {
|
|
var displayRect;
|
|
if (!brushCtx) {
|
|
return;
|
|
}
|
|
ensureCanvasInCropper();
|
|
updateCanvasSize();
|
|
displayRect = getDisplayRect();
|
|
if (!displayRect || !displayRect.width || !displayRect.height) {
|
|
return;
|
|
}
|
|
brushCtx.clearRect(0, 0, brushCanvas.width, brushCanvas.height);
|
|
if (!brushData.points.length) {
|
|
syncBrushData();
|
|
renderBrushStatus();
|
|
return;
|
|
}
|
|
|
|
var scaleX = displayRect.width / displayRect.naturalWidth;
|
|
var scaleY = displayRect.height / displayRect.naturalHeight;
|
|
$.each(brushData.points, function (i, point) {
|
|
var drawX = displayRect.left + point.x * scaleX;
|
|
var drawY = displayRect.top + point.y * scaleY;
|
|
var drawR = Math.max(3, point.r * ((scaleX + scaleY) / 2));
|
|
brushCtx.fillStyle = point.c || brushData.color;
|
|
brushCtx.globalAlpha = 0.45;
|
|
brushCtx.beginPath();
|
|
brushCtx.arc(drawX, drawY, drawR, 0, Math.PI * 2, false);
|
|
brushCtx.fill();
|
|
});
|
|
brushCtx.globalAlpha = 1;
|
|
|
|
syncBrushData();
|
|
renderBrushStatus();
|
|
}
|
|
|
|
function renderBrushStatus() {
|
|
if (!$brushStatus.length) {
|
|
return;
|
|
}
|
|
if (!brushData.enabled) {
|
|
$brushStatus.text('未开启画笔');
|
|
return;
|
|
}
|
|
$brushStatus.text('已绘制 ' + brushData.points.length + ' 笔,按住鼠标左键在图上涂抹');
|
|
}
|
|
|
|
function toNaturalPoint(e) {
|
|
var displayRect = getDisplayRect();
|
|
var rect;
|
|
if (!displayRect || !displayRect.width || !displayRect.height || !brushCanvas) {
|
|
return null;
|
|
}
|
|
rect = brushCanvas.getBoundingClientRect();
|
|
var relX = e.clientX - rect.left;
|
|
var relY = e.clientY - rect.top;
|
|
if (relX < displayRect.left || relY < displayRect.top || relX > displayRect.left + displayRect.width || relY > displayRect.top + displayRect.height) {
|
|
return null;
|
|
}
|
|
var naturalX = Math.round((relX - displayRect.left) * displayRect.naturalWidth / displayRect.width);
|
|
var naturalY = Math.round((relY - displayRect.top) * displayRect.naturalHeight / displayRect.height);
|
|
return {
|
|
x: Math.max(0, Math.min(displayRect.naturalWidth - 1, naturalX)),
|
|
y: Math.max(0, Math.min(displayRect.naturalHeight - 1, naturalY))
|
|
};
|
|
}
|
|
|
|
function addBrushPoint(e) {
|
|
var point = toNaturalPoint(e);
|
|
var prev;
|
|
var dx;
|
|
var dy;
|
|
if (!point) {
|
|
return;
|
|
}
|
|
point.r = Math.max(4, Math.round(brushData.size / 2));
|
|
point.c = brushData.color;
|
|
prev = brushData.points.length ? brushData.points[brushData.points.length - 1] : null;
|
|
if (prev) {
|
|
dx = point.x - prev.x;
|
|
dy = point.y - prev.y;
|
|
if ((dx * dx + dy * dy) < (point.r * point.r) / 6) {
|
|
return;
|
|
}
|
|
}
|
|
brushData.points.push(point);
|
|
renderBrushOverlay();
|
|
}
|
|
|
|
function setBrushMode(enabled) {
|
|
brushData.enabled = !!enabled;
|
|
if (brushData.enabled) {
|
|
$brushCanvas.show().css('cursor', 'crosshair');
|
|
$toggleBrush.text('关闭画笔').removeClass('btn-warning').addClass('btn-success');
|
|
} else {
|
|
isDrawing = false;
|
|
$brushCanvas.hide();
|
|
$toggleBrush.text('开启画笔').removeClass('btn-success').addClass('btn-warning');
|
|
}
|
|
renderBrushOverlay();
|
|
}
|
|
|
|
|
|
// Tooltip
|
|
$('[data-toggle="tooltip"]').tooltip();
|
|
|
|
|
|
// Cropper
|
|
$image.on({
|
|
'build.cropper': function (e) {
|
|
console.log(e.type);
|
|
},
|
|
'built.cropper': function (e) {
|
|
console.log(e.type);
|
|
ensureCanvasInCropper();
|
|
renderBrushOverlay();
|
|
},
|
|
'cropstart.cropper': function (e) {
|
|
console.log(e.type, e.action);
|
|
},
|
|
'cropmove.cropper': function (e) {
|
|
console.log(e.type, e.action);
|
|
},
|
|
'cropend.cropper': function (e) {
|
|
console.log(e.type, e.action);
|
|
},
|
|
'crop.cropper': function (e) {
|
|
console.log(e.type, e.x, e.y, e.width, e.height, e.rotate, e.scaleX, e.scaleY);
|
|
renderBrushOverlay();
|
|
},
|
|
'zoom.cropper': function (e) {
|
|
console.log(e.type, e.ratio);
|
|
setTimeout(renderBrushOverlay, 0);
|
|
}
|
|
}).cropper(options);
|
|
|
|
|
|
// Buttons
|
|
if (!$.isFunction(document.createElement('canvas').getContext)) {
|
|
$('button[data-method="getCroppedCanvas"]').prop('disabled', true);
|
|
}
|
|
|
|
if (typeof document.createElement('cropper').style.transition === 'undefined') {
|
|
$('button[data-method="rotate"]').prop('disabled', true);
|
|
$('button[data-method="scale"]').prop('disabled', true);
|
|
}
|
|
|
|
|
|
|
|
|
|
// Options
|
|
$('.docs-toggles').on('change', 'input', function () {
|
|
var $this = $(this);
|
|
var name = $this.attr('name');
|
|
var type = $this.prop('type');
|
|
var cropBoxData;
|
|
var canvasData;
|
|
|
|
if (!$image.data('cropper')) {
|
|
return;
|
|
}
|
|
|
|
if (type === 'checkbox') {
|
|
options[name] = $this.prop('checked');
|
|
cropBoxData = $image.cropper('getCropBoxData');
|
|
canvasData = $image.cropper('getCanvasData');
|
|
|
|
options.built = function () {
|
|
$image.cropper('setCropBoxData', cropBoxData);
|
|
$image.cropper('setCanvasData', canvasData);
|
|
};
|
|
} else if (type === 'radio') {
|
|
options[name] = $this.val();
|
|
}
|
|
|
|
$image.cropper('destroy').cropper(options);
|
|
});
|
|
|
|
|
|
// Methods
|
|
$('.docs-buttons').on('click', '[data-method]', function () {
|
|
var $this = $(this);
|
|
var data = $this.data();
|
|
var $target;
|
|
var result;
|
|
|
|
if ($this.prop('disabled') || $this.hasClass('disabled')) {
|
|
return;
|
|
}
|
|
|
|
if ($image.data('cropper') && data.method) {
|
|
data = $.extend({}, data); // Clone a new one
|
|
|
|
if (typeof data.target !== 'undefined') {
|
|
$target = $(data.target);
|
|
|
|
if (typeof data.option === 'undefined') {
|
|
try {
|
|
data.option = JSON.parse($target.val());
|
|
} catch (e) {
|
|
console.log(e.message);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (data.method === 'rotate') {
|
|
$image.cropper('clear');
|
|
}
|
|
|
|
result = $image.cropper(data.method, data.option, data.secondOption);
|
|
|
|
if (data.method === 'rotate') {
|
|
$image.cropper('crop');
|
|
}
|
|
|
|
switch (data.method) {
|
|
case 'scaleX':
|
|
case 'scaleY':
|
|
$(this).data('option', -data.option);
|
|
break;
|
|
|
|
case 'getCroppedCanvas':
|
|
if (result) {
|
|
|
|
// Bootstrap's Modal
|
|
$('#getCroppedCanvasModal').modal().find('.modal-body').html(result);
|
|
|
|
if (!$download.hasClass('disabled')) {
|
|
$download.attr('href', result.toDataURL('image/jpeg'));
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if ($.isPlainObject(result) && $target) {
|
|
try {
|
|
$target.val(JSON.stringify(result));
|
|
} catch (e) {
|
|
console.log(e.message);
|
|
}
|
|
}
|
|
|
|
}
|
|
});
|
|
|
|
$toggleBrush.on('click', function () {
|
|
setBrushMode(!brushData.enabled);
|
|
});
|
|
|
|
$('#clearBrush').on('click', function () {
|
|
brushData.points = [];
|
|
renderBrushOverlay();
|
|
});
|
|
|
|
$brushSize.on('input change', function () {
|
|
brushData.size = parseInt($(this).val(), 10) || 24;
|
|
$brushSizeText.text(brushData.size);
|
|
syncBrushData();
|
|
renderBrushOverlay();
|
|
});
|
|
|
|
$brushColor.on('change input', function () {
|
|
brushData.color = $(this).val() || '#ff0000';
|
|
syncBrushData();
|
|
renderBrushOverlay();
|
|
});
|
|
|
|
$brushCanvas.on('mousedown', function (e) {
|
|
if (!brushData.enabled || e.which !== 1) {
|
|
return;
|
|
}
|
|
isDrawing = true;
|
|
addBrushPoint(e);
|
|
e.preventDefault();
|
|
}).on('mousemove', function (e) {
|
|
if (!brushData.enabled || !isDrawing) {
|
|
return;
|
|
}
|
|
addBrushPoint(e);
|
|
e.preventDefault();
|
|
}).on('mouseup mouseleave', function () {
|
|
isDrawing = false;
|
|
});
|
|
|
|
$(window).on('resize', function () {
|
|
renderBrushOverlay();
|
|
});
|
|
|
|
updateCanvasSize();
|
|
renderBrushOverlay();
|
|
renderBrushStatus();
|
|
|
|
|
|
// Keyboard
|
|
$(document.body).on('keydown', function (e) {
|
|
|
|
if (!$image.data('cropper') || this.scrollTop > 300) {
|
|
return;
|
|
}
|
|
|
|
switch (e.which) {
|
|
case 37:
|
|
e.preventDefault();
|
|
$image.cropper('move', -1, 0);
|
|
break;
|
|
|
|
case 38:
|
|
e.preventDefault();
|
|
$image.cropper('move', 0, -1);
|
|
break;
|
|
|
|
case 39:
|
|
e.preventDefault();
|
|
$image.cropper('move', 1, 0);
|
|
break;
|
|
|
|
case 40:
|
|
e.preventDefault();
|
|
$image.cropper('move', 0, 1);
|
|
break;
|
|
}
|
|
|
|
});
|
|
|
|
|
|
// Import image
|
|
var $inputImage = $('#inputImage');
|
|
var URL = window.URL || window.webkitURL;
|
|
var blobURL;
|
|
|
|
if (URL) {
|
|
$inputImage.change(function () {
|
|
var files = this.files;
|
|
var file;
|
|
|
|
if (!$image.data('cropper')) {
|
|
return;
|
|
}
|
|
|
|
if (files && files.length) {
|
|
file = files[0];
|
|
|
|
if (/^image\/\w+$/.test(file.type)) {
|
|
blobURL = URL.createObjectURL(file);
|
|
$image.one('built.cropper', function () {
|
|
|
|
// Revoke when load complete
|
|
URL.revokeObjectURL(blobURL);
|
|
}).cropper('reset').cropper('replace', blobURL);
|
|
$inputImage.val('');
|
|
} else {
|
|
window.alert('Please choose an image file.');
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
$inputImage.prop('disabled', true).parent().addClass('disabled');
|
|
}
|
|
|
|
});
|