我希望通常在移动设备上使用捏/缩放手势来调整FabricJS文本框的大小。 据我了解,如果您包含“手势包”,FabricJS会默认实现此方法。
一个特殊的问题是,如果您开始手势,则文本框只会缩小并且无法将其调整大小。 您可以以此尺寸进行测试:
https://www.grabstein-steinmetz.de/phone/editor.html
我怀疑这个问题与html meta标签有关。我尝试了不同的版本,有时调整大小的行为是正确的。这些外观非常不一致,所以我无法识别错误。
画布本身位于flex div内。
$(function(){
$('#sendAlert').hide(); //initial
$('#deleteSelected').prop('disabled', true); //initial
//makes sure that the custom font works
WebFont.load({
custom: {
families: ['PresidentPlain']
},
active: fontsLoaded
});
var curFont = 'PresidentPlain';
var curColor = '#000000';
var canvas = new fabric.Canvas('canvas');
var imgElement = document.getElementById('image');
function fontsLoaded(){
//initial setup of canvas w/img from url as background
fabric.Image.fromURL('https://www.grabstein-steinmetz.de/images/urnengrabstein%20258-crop-u116972.jpg', function(img) {
//scale img & canvas: keep image ratio but stay inside container width & height
var ratio = img.width / img.height;
var width = $(window).width();
var height = width * ratio;
if(height > $(window).height()){
height = $(window).height();
width = height / ratio;
}
canvas.setWidth(width)
canvas.setHeight(height);
canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas), {
scaleX: canvas.width / img.width,
scaleY: canvas.height / img.height
});
});
fabric.util.addListener(canvas.upperCanvasEl, 'dblclick', function (e) {
//create new textbox with values from legend
if(canvas.findTarget(e) === undefined){
var textbox = new fabric.Textbox('', {
left: canvas.getPointer(e).x,
top: canvas.getPointer(e).y,
fontSize: 20,
fontFamily: curFont,
fill: curColor,
borderColor: 'grey',
cornerColor: '#2E2E2E',
cornerSize: 6,
transparentCorners: false,
lockUniScaling: true
});
canvas.add(textbox).setActiveObject(textbox);
textbox.enterEditing();
}
});
canvas.on("text:editing:exited", function (e) {
//delete textbox when left empty
if(e.target.text == ''){
canvas.remove(e.target);
}
});
canvas.on('selection:created', function (e) {
$("#deleteSelected").prop('disabled', true);
updateLegend(e);
});
canvas.on('selection:updated', function(e){
updateLegend(e);
})
canvas.on('selection:cleared', function () {
$("#deleteSelected").prop('disabled', false);
});
function updateLegend(e){
//set legend according to selection values
if(e.target.fontFamily === "Arial"){
$('input[name=font][value=Arial]').prop('checked', true);
}else{
$('input[name=font][value=PresidentPlain]').prop('checked', true);
}
$('.colorpallet > div').each(function(el){
$(this).removeClass('coloractive');
})
if(e.target.fill === "#000000"){
$('.colorblack').addClass('coloractive');
curColor = $('.colorblack').attr('curColor');
}else if(e.target.fill === "#FFFFFF"){
$('.colorwhite').addClass('coloractive');
curColor = $('.colorwhite').attr('curColor');
}else{
$('.colorbrown').addClass('coloractive');
curColor = $('.colorbrown').attr('curColor');
}
}
$('#deleteSelected').click(function(){
//delete selected textboxes in canvas
var selection = canvas.getActiveObject();
if(selection){
if (selection.type === 'activeSelection') {
selection.forEachObject(function(element) {
canvas.remove(element);
});
}else{
canvas.remove(selection);
}
canvas.discardActiveObject();
canvas.requestRenderAll();
}
});
/*MODAL*/
$('#btnDownload').click(function(){
canvas.discardActiveObject();
canvas.requestRenderAll();
$('#canvas').get(0).toBlob(function(blob){
saveAs(blob, "Grabstein.png");
})
});
$('#sendBtn').click(function(){
$('#modalBtn').prop('disabled', true);
$('#canvas').get(0).toBlob(function(blob){
var fd = new FormData();
fd.append('img', blob);
fd.append('name', $('#input_name').val());
fd.append('email', $('#input_mail').val());
fd.append('phone', $('#input_phone').val());
fd.append('description', $('#input_message').val());
$.ajax({
type: 'POST',
url: '../scripts/editormail.php',
data: fd,
processData: false,
contentType: false
}).done(function(data) {
$('#modalBtn').prop('disabled', false);
console.log(data);
$('#sendAlert').show();
});
});
});
$('.modal').on('keyup paste', function(){
//validate form input
if($('#input_name').val() && $('#input_mail').val() && validateEmail($('#input_mail').val()) && $('#input_message').val() && $('#input_phone').val()){
$('#sendBtn').prop('disabled', false);
}else{
$('#sendBtn').prop('disabled', true);
}
})
/* LEGEND */
$('input[type=radio][name=font]').change(function(){
//change current fontfamily & set fontfamily of selected in canvas
curFont = this.value;
var selection = canvas.getActiveObject();
if(selection){
if (selection.type === 'activeSelection') {
selection.forEachObject(function(element) {
element.set('fontFamily', curFont);
});
}else{
selection.set('fontFamily', curFont);
}
canvas.requestRenderAll();
}
})
$('.colorpallet > div').click(function(){
//change current fontcolor & set fontcolor of selected in canvas
$('.colorpallet > div').each(function(el){
$(this).removeClass('coloractive');
})
$(this).addClass('coloractive');
curColor = $(this).attr('curColor');
var selection = canvas.getActiveObject();
if(selection){
if(selection.type === 'activeSelection') {
selection.forEachObject(function(element) {
element.set('fill', curColor);
});
}
else{
selection.set('fill', curColor);
}
canvas.requestRenderAll();
}
});
}
//retrieve get Parameter from url
function findGetParameter(parameterName) {
var result = null,
tmp = [];
location.search
.substr(1)
.split("&")
.forEach(function (item) {
tmp = item.split("=");
if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]);
});
return result;
}
function validateEmail(email) {
var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(email).toLowerCase());
}
});
@font-face {
font-family: "PresidentPlain";
src: url("../fonts/president.eot");
src: url("./fonts/president.eot?#iefix") format("embedded-opentype"),
url("../fonts/president.otf") format("opentype"),
url("../fonts/president.svg") format("svg"),
url("../fonts/president.ttf") format("truetype"),
url("../fonts/president.woff") format("woff"),
url("../fonts/president.woff2") format("woff2");
font-weight: normal;
font-style: normal;
}
body{
font-family: "Arial";
}
p{
margin: 0;
}
.fontArial {
font-size: 30px;
}
.fontPresident {
font-family: 'PresidentPlain';
font-size: 30px;
}
.canvasWrapper {
width: 100vw;
height: 100vh;
display: flex;
flex-flow: row wrap;
justify-content: center;
align-items: center;
}
.legendWrapper {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
.legendItems {
width: 100%;
}
/* SLIDER/RADIOSELECT @ FONT */
.radiosliderwrapper {
width: 100%;
float: left;
}
.pdx-radio-slider {
border: 2px solid #0077BA;
display: inline-flex;
position: relative;
box-sizing: border-box;
width: 100%;
}
.pdx-radio-slider .pdx-radio-slider__option {
display: none;
}
.pdx-radio-slider label {
z-index: 1;
transition: color 0.4s ease;
display: flex;
color: #0077BA;
width: 50%;
height: 30px;
margin: 7px;
cursor: pointer;
justify-content: center;
align-items: center;
}
.pdx-radio-slider .pdx-radio-slider__option:checked + label {
color: #fff;
}
.pdx-radio-slider .pdx-radio-slider__option:nth-child(1):checked ~ .pdx-radio-slider__slider {
left: 0;
}
.pdx-radio-slider .pdx-radio-slider__option:nth-child(3):checked ~ .pdx-radio-slider__slider {
left: 50%;
}
.pdx-radio-slider__slider {
z-index: 0;
width: 50%;
height: 48px;
position: absolute;
top: -2px;
background-color: #0077BA;
transition: left 0.4s ease;
}
/* //SLIDER/RADIOSELECT @ FONT */
/* COLORPALLET */
.colorpallet {
margin-top: 20px;
width: 100%;
float: left;
background-color: #0077BA;
}
.colorwhite, .colorblack, .colorbrown {
width: 21%;
margin: 6%;
float: left;
}
.colorwhite:before, .colorblack:before, .colorbrown:before {
content: "";
display: block;
padding-top: 100%; /* initial ratio of 1:1*/
}
.colorwhite{
background-color: white;
}
.colorwhite:hover, .colorwhite.coloractive {
background-color: #979A9A;
}
.colorbrown {
background-color: #6E2C00;
}
.colorbrown:hover, .colorbrown.coloractive {
background-color: #E67E22;
}
.colorblack {
background-color: black;
}
.colorblack:hover, .colorblack.coloractive {
background-color: #353535;
}
/* //COLORPALLET */
.noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome and Opera */
}
.helpermsg{
width: 100%;
float: left;
text-align: center;
background-color: #0077BA;
border-top: 1px white solid;
color: white;
height: 40px;
line-height: 40px;
font-size: 15px;
}
<meta http-equiv="Content-type" content="text/html;charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0 shrink-to-fit=no"/>
<!DOCTYPE html>
<head>
<script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script>
<script type="text/javascript" src="../scripts/fabric.min.js"></script>
<script src="https://fastcdn.org/FileSaver.js/1.1.20151003/FileSaver.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/javascript-canvas-to-blob/3.14.0/js/canvas-to-blob.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
</head>
<body>
<!-- 2D editor -->
<div id="sendAlert" class="alert alert-success hidden" role="alert">
Vielen Dank! Wir haben Ihr Bild erhalten. <a href="javascript:history.back();" class="alert-link">Hier kommen Sie wieder zur Galerie</a>.
</div>
<div id="canvasWrapper" class="canvasWrapper">
<canvas id="canvas"></canvas>
</div>
<div class="legendWrapper">
<div class="legendItems">
<div class="radiosliderwrapper">
<div class="pdx-radio-slider">
<input type="radio" class="pdx-radio-slider__option" id="first_toggle" name="font" value="Arial">
<label for="first_toggle" class="fontArial noselect">Arial</label>
<input type="radio" checked class="pdx-radio-slider__option" id="second_toggle" name="font" value="PresidentPlain">
<label for="second_toggle" class="fontPresident noselect">President</label>
<div class="pdx-radio-slider__slider">
</div>
</div>
</div>
<div class="colorpallet">
<div class="colorblack coloractive" curColor="#000000"></div>
<div class="colorwhite" curColor="#FFFFFF"></div>
<div class="colorbrown" curColor="#6E2C00"></div>
</div>
<span class="helpermsg noselect">Für neues Textfeld: DOPPELKLICK auf Bild</span>
<div id="deleteSelected" class="btn btn-danger noselect mt-3" style=" width: 100%;">ausgewählte Textfelder löschen</div>
<div class="btn btn-success noselect mb-5 mt-3" style="width: 100%;" data-toggle="modal" data-target="#exampleModal" id="modalBtn">speichern/abschicken</div>
</div>
</div>
</body>
</html>