Я создал приложение angularJS, в этом приложении SVG файлы представляют одежду, которую пользователь выбирает. У меня есть кнопка загрузки, которая (в настоящее время) сохраняет первый SVG как PNG в базу данных, и я использую представление для отображения этого "предварительного просмотра".
Директива, которую я создал, выглядит следующим образом:
.directive('kdExport', function () {
return {
restrict: 'A',
scope: {
target: '@kdExport',
team: '='
},
controller: 'ExportImageController',
link: function (scope, element, attrs, controller) {
console.log(scope.team);
// Bind to the onclick event of our button
element.bind('click', function (e) {
// Prevent the default action
e.preventDefault();
// Generate the image
controller.generateImage(scope.target, scope.team, function (preview) {
// Create our url
var url = '/kits/preview/' + preview.id;
// Open a new window
window.open(url, '_blank');
});
});
}
};
})
и контроллер выглядит следующим образом:
.controller('ExportImageController', ['PreviewService', function (service) {
var self = this;
// Function to remove the hidden layers of an SVG document
var removeHidden = function (element) {
// Get the element children
var children = element.children(),
i = children.length;
// If we have any children
if (children.length) {
// For each child
for (i; i >= 0; i--) {
// Get our child
var child = angular.element(children[i - 1]);
// Remove hidden from the child children
removeHidden(child);
// Finally, if this child has the class "hidden"
if (child.hasClass("hidden")) {
// Remove the child
child.remove();
}
}
}
};
// Public function to generate the image
self.generateImage = function (element, team, onSuccess) {
// Get our SVG
var target = document.getElementById(element),
container = target.getElementsByClassName('svg-document')[0],
clone = container.cloneNode(true);
// Remove hidden layers
removeHidden(angular.element(clone));
// Create our data
var data = clone.innerHTML,
svg = new Blob([data], { type: 'image/svg+xml;charset=utf-8' });
// Get our context
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
// Create our image
var DOMURL = window.URL || window.webkitURL || window,
url = DOMURL.createObjectURL(svg),
img = new Image();
// When the image has loaded
img.onload = function () {
canvas.width = 1000;
canvas.height = 500;
// Draw our image using the context
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, 1000, 500);
DOMURL.revokeObjectURL(url);
// Get our URL as a base64 string
var dataURL = canvas.toDataURL("image/png");
// Create our model
var model = {
teamName: team.name,
sport: team.sport,
data: dataURL
};
// Create our preview
service.create(model).then(function (response) {
// Invoke our success callback
onSuccess(response);
});
}
// Set the URL of the image
img.src = url;
};
}])
Это отлично подходит для одного документа SVG, но теперь клиент попросил меня сделать это для нескольких SVG с заголовком под каждым, и они хотят все это в одном PNG. Я не проделал много работы с canvasing, поэтому я не уверен, что это можно сделать. Кто-нибудь знает, как я могу это достичь?
Хорошо, поэтому я сам это понял, используя обещания. В основном я создал метод drawImage, который позволил мне нарисовать изображение для каждого SVG. Чтобы убедиться, что все изображения были нарисованы до того, как я вызову toDataURL, я заставил функцию вернуть обещание, и после загрузки изображения я решил это обещание. Затем я просто использовал $ q.all, чтобы получить dataURL и сохранить данные в моей базе данных. Методы выглядели так:
// Private function for drawing our images
var drawImage = function (canvas, ctx, clone) {
// Defer our promise
var deferred = $q.defer();
// Create our data
var data = clone.innerHTML,
svg = new Blob([data], { type: 'image/svg+xml;charset=utf-8' });
// Create our image
var DOMURL = window.URL || window.webkitURL || window,
url = DOMURL.createObjectURL(svg),
img = new Image();
// When the image has loaded
img.onload = function () {
// Get our location
getNextLocation(canvas.width, canvas.height, img);
// Draw our image using the context (Only draws half the image because I don't want to show the back)
ctx.drawImage(img, 0, 0, img.width / 2, img.height, location.x, location.y, location.width, location.height);
DOMURL.revokeObjectURL(url);
// Resolve our promise
deferred.resolve();
}
// Set the URL of the image
img.src = url;
// Return our promise
return deferred.promise;
};
// Public function to generate the image
self.generateImage = function (element, team, onSuccess) {
// Get our SVG
var target = document.getElementById('totals'),
containers = angular.element(target.getElementsByClassName('svg-document'));
// Get our context
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
// Set our canvas height and width
canvas.width = 2000;
canvas.height = calculateCanvasHeight(containers.length);
// Create our array of promises
var promises = [];
// For each container
for (var i = 0; i < containers.length; i++) {
// Get our container
var container = containers[i],
clone = container.cloneNode(true);
// Remove hidden layers
removeHidden(angular.element(clone));
// Add our promise to the array
promises.push(drawImage(canvas, ctx, clone));
}
// When all promises have resolve
$q.all(promises).then(function () {
// Get our URL as a base64 string
var dataURL = canvas.toDataURL("image/png");
// Create our model
var model = {
teamName: team.name,
sport: team.sport,
data: dataURL
};
// Create our preview
self.create(model).then(function (response) {
// Invoke our success callback
onSuccess(response);
});
})
};
Очевидно, что здесь отсутствует код, но этот код отвечает на мою проблему, остальное просто заставляет мою службу работать :)
variable === svgDocs.length-1
. Кроме того, вам не нужно создавать Blobs и ObjectUrls для рисования ваших svgs, достаточно простого dataURL (вы можете использовать'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(new XMLSerializer().serializeToString(yourSVGElement))
).