Last active
December 31, 2023 16:22
-
-
Save karandatwani92/9212ce77dd389be96395866b0ae3780d to your computer and use it in GitHub Desktop.
Webcam field for Backpack(Cropper.js + Webcam.js)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@php | |
$field['wrapper'] = $field['wrapper'] ?? $field['wrapperAttributes'] ?? []; | |
$field['wrapper']['class'] = $field['wrapper']['class'] ?? "form-group col-sm-12"; | |
$field['wrapper']['class'] = $field['wrapper']['class'].' cropperImage'; | |
$field['wrapper']['data-aspectRatio'] = $field['aspect_ratio'] ?? 0; | |
$field['wrapper']['data-crop'] = $field['crop'] ?? false; | |
$field['wrapper']['data-field-name'] = $field['wrapper']['data-field-name'] ?? $field['name']; | |
$field['wrapper']['data-init-function'] = $field['wrapper']['data-init-function'] ?? 'bpFieldInitWebcamCropperImageElement'; | |
$empty_pixel = "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="; | |
// calculate the value of the hidden input | |
if (!is_null(old(square_brackets_to_dots($field['name'])))) { | |
$value = old(square_brackets_to_dots($field['name'])); | |
} elseif(isset($field['src']) && isset($entry)) { | |
$value = $entry->find($entry->id)->{$field['src']}(); | |
} else { | |
$value = old_empty_or_null($field['name'], '') ?? $field['value'] ?? $field['default'] ?? ''; | |
} | |
@endphp | |
@include('crud::fields.inc.wrapper_start') | |
<label>{!! $field['label'] !!}</label> | |
@include('crud::fields.inc.translatable_icon') | |
{{-- Wrap the image or canvas element with a block element (container) --}} | |
<div class="row"> | |
<div class="col-sm-6" data-handle="previewArea" style="margin-bottom: 20px;"> | |
<img data-handle="mainImage" src=""> | |
</div> | |
@if(isset($field['crop']) && $field['crop']) | |
<div class="col-sm-3" data-handle="previewArea"> | |
<div class="docs-preview clearfix"> | |
<div class="img-preview preview-lg"> | |
<img src="" style="display: block; min-width: 0px !important; min-height: 0px !important; max-width: none !important; max-height: none !important; margin-left: -32.875px; margin-top: -18.4922px; transform: none;"> | |
</div> | |
</div> | |
</div> | |
@endif | |
</div> | |
<div class="btn-group"> | |
<div class="btn btn-light btn-sm btn-file"> | |
{{ trans('backpack::crud.choose_file') }} <input type="file" accept="image/*" data-handle="uploadImage" @include('crud::fields.inc.attributes', ['default_class' => 'form-control'])> | |
<input type="hidden" data-handle="hiddenImage" name="{{ $field['name'] }}" value="{{ $value?$value:$empty_pixel }}"> | |
</div> | |
@if(isset($field['crop']) && $field['crop']) | |
<button class="btn btn-light btn-sm" data-handle="rotateLeft" type="button" style="display: none;"><i class="la la-rotate-left"></i></button> | |
<button class="btn btn-light btn-sm" data-handle="rotateRight" type="button" style="display: none;"><i class="la la-rotate-right"></i></button> | |
<button class="btn btn-light btn-sm" data-handle="zoomIn" type="button" style="display: none;"><i class="la la-search-plus"></i></button> | |
<button class="btn btn-light btn-sm" data-handle="zoomOut" type="button" style="display: none;"><i class="la la-search-minus"></i></button> | |
<button class="btn btn-light btn-sm" data-handle="reset" type="button" style="display: none;"><i class="la la-times"></i></button> | |
@endif | |
<button class="btn btn-light btn-sm" data-handle="remove" type="button"><i class="la la-trash"></i></button> | |
</div> | |
{{-- HINT --}} | |
@if (isset($field['hint'])) | |
<p class="help-block">{!! $field['hint'] !!}</p> | |
@endif | |
<input type="hidden" class="hiddenFilename" name="{{ $field['filename'] }}" value=""> | |
@include('crud::fields.inc.wrapper_end') | |
{{-- ########################################## --}} | |
{{-- Extra CSS and JS for this particular field --}} | |
{{-- If a field type is shown multiple times on a form, the CSS and JS will only be loaded once --}} | |
{{-- FIELD CSS - will be loaded in the after_styles section --}} | |
@push('crud_fields_styles') | |
@basset('https://unpkg.com/cropperjs@1.5.13/dist/cropper.min.css') | |
@bassetBlock('backpack/pro/fields/webcam-image-field.css') | |
<style> | |
.hide { | |
display: none; | |
} | |
.image .btn-group { | |
margin-top: 10px; | |
} | |
img { | |
max-width: 100%; /* This rule is very important, please do not ignore this! */ | |
} | |
.img-container, .img-preview { | |
width: 100%; | |
text-align: center; | |
} | |
.img-preview { | |
float: left; | |
margin-right: 10px; | |
margin-bottom: 10px; | |
overflow: hidden; | |
} | |
.preview-lg { | |
width: 263px; | |
height: 148px; | |
} | |
.btn-file { | |
position: relative; | |
overflow: hidden; | |
} | |
.btn-file input[type=file] { | |
position: absolute; | |
top: 0; | |
right: 0; | |
min-width: 100%; | |
min-height: 100%; | |
font-size: 100px; | |
text-align: right; | |
filter: alpha(opacity=0); | |
opacity: 0; | |
outline: none; | |
background: white; | |
cursor: inherit; | |
display: block; | |
} | |
</style> | |
@endBassetBlock | |
@endpush | |
{{-- FIELD JS - will be loaded in the after_scripts section --}} | |
@push('crud_fields_scripts') | |
@basset('https://unpkg.com/cropperjs@1.5.13/dist/cropper.min.js') | |
@basset('https://unpkg.com/jquery-cropper@1.0.1/dist/jquery-cropper.min.js') | |
@basset('https://cdnjs.cloudflare.com/ajax/libs/webcamjs/1.0.26/webcam.min.js') | |
@bassetBlock('backpack/pro/fields/webcam-image-field.js') | |
<script> | |
function bpFieldInitWebcamCropperImageElement(element) { | |
// Find DOM elements under this form-group element | |
var $mainImage = element.find('[data-handle=mainImage]'); | |
var $uploadImage = element.find("[data-handle=uploadImage]"); | |
var $hiddenImage = element.find("[data-handle=hiddenImage]"); | |
var $hiddenFilename = element.find(".hiddenFilename"); | |
var $rotateLeft = element.find("[data-handle=rotateLeft]"); | |
var $rotateRight = element.find("[data-handle=rotateRight]"); | |
var $zoomIn = element.find("[data-handle=zoomIn]"); | |
var $zoomOut = element.find("[data-handle=zoomOut]"); | |
var $reset = element.find("[data-handle=reset]"); | |
var $remove = element.find("[data-handle=remove]"); | |
var $previews = element.find("[data-handle=previewArea]"); | |
// Options either global for all image type fields, or use 'data-*' elements for options passed in via the CRUD controller | |
var options = { | |
viewMode: 2, | |
checkOrientation: false, | |
autoCropArea: 1, | |
responsive: true, | |
aspectRatio : element.attr('data-aspectRatio') | |
}; | |
var crop = element.attr('data-crop'); | |
// Hide 'Remove' button if there is no image saved | |
if (!$hiddenImage.val()){ | |
$previews.hide(); | |
$remove.hide(); | |
} else { //if there is an image | |
$mainImage.attr('src', $hiddenImage.val()); | |
$mainImage.cropper(options).cropper("reset", true).cropper("replace", $mainImage.attr('src')); | |
$('form').submit(function() { | |
var imageURL = $mainImage.cropper('getCroppedCanvas').toDataURL("image/jpeg"); | |
$hiddenImage.val(imageURL); | |
return true; | |
}); | |
$rotateLeft.show(); | |
$rotateRight.show(); | |
$zoomIn.show(); | |
$zoomOut.show(); | |
$reset.show(); | |
$remove.show(); | |
} | |
// Make the main image show the image in the hidden input | |
$mainImage.attr('src', $hiddenImage.val()); | |
var $webcam_element = element.find('.img-preview')[0]; | |
Webcam.attach($webcam_element); | |
$($webcam_element).click(function() { | |
Webcam.snap(function(data_uri) { | |
$uploadImage.val(""); | |
$previews.show(); | |
if (crop) { | |
$mainImage.cropper(options).cropper("reset", true).cropper("replace", data_uri); | |
// Override form submit to copy canvas to hidden input before submitting | |
$mainImage.on('ready cropstart cropend', function() { | |
var imageURL = $mainImage.cropper('getCroppedCanvas').toDataURL( | |
"image/jpeg"); | |
$hiddenImage.val(imageURL); | |
return true; | |
}); | |
$rotateLeft.show(); | |
$rotateRight.show(); | |
$zoomIn.show(); | |
$zoomOut.show(); | |
$reset.show(); | |
$remove.show(); | |
} else { | |
$mainImage.attr('src', data_uri); | |
$hiddenImage.val(data_uri); | |
$remove.show(); | |
} | |
}); | |
}); | |
// Only initialize cropper plugin if crop is set to true | |
if(crop){ | |
$remove.click(function() { | |
$mainImage.cropper("destroy"); | |
$mainImage.attr('src',''); | |
$hiddenImage.val(''); | |
if (filename == "true"){ | |
$hiddenFilename.val('removed'); | |
} | |
$rotateLeft.hide(); | |
$rotateRight.hide(); | |
$zoomIn.hide(); | |
$zoomOut.hide(); | |
$reset.hide(); | |
$remove.hide(); | |
$previews.hide(); | |
}); | |
} else { | |
$remove.click(function() { | |
$mainImage.attr('src',''); | |
$hiddenImage.val(''); | |
$hiddenFilename.val('removed'); | |
$remove.hide(); | |
$previews.hide(); | |
}); | |
} | |
//Set hiddenFilename field to 'removed' if image has been removed. | |
//Otherwise hiddenFilename will be null if no changes have been made. | |
$uploadImage.change(function() { | |
var fileReader = new FileReader(), | |
files = this.files, | |
file; | |
if (!files.length) { | |
return; | |
} | |
file = files[0]; | |
if (/^image\/\w+$/.test(file.type)) { | |
$hiddenFilename.val(file.name); | |
fileReader.readAsDataURL(file); | |
fileReader.onload = function () { | |
$uploadImage.val(""); | |
$previews.show(); | |
if(crop){ | |
$mainImage.cropper(options).cropper("reset", true).cropper("replace", this.result); | |
// update the hidden input after selecting a new item or cropping | |
$mainImage.on('ready cropstart cropend', function() { | |
var imageURL = $mainImage.cropper('getCroppedCanvas').toDataURL(file.type); | |
$hiddenImage.val(imageURL); | |
return true; | |
}); | |
$rotateLeft.show(); | |
$rotateRight.show(); | |
$zoomIn.show(); | |
$zoomOut.show(); | |
$reset.show(); | |
$remove.show(); | |
} else { | |
$mainImage.attr('src',this.result); | |
$hiddenImage.val(this.result); | |
$remove.show(); | |
} | |
}; | |
} else { | |
new Noty({ | |
type: "error", | |
text: "<strong>Please choose an image file</strong><br>The file you've chosen does not look like an image." | |
}).show(); | |
} | |
}); | |
//moved the click binds outside change event, or we would register as many click events for the same amout of times | |
//we triggered the image change | |
if(crop) { | |
$rotateLeft.click(function() { | |
$mainImage.cropper("rotate", 90); | |
$mainImage.trigger('cropend'); | |
}); | |
$rotateRight.click(function() { | |
$mainImage.cropper("rotate", -90); | |
$mainImage.trigger('cropend'); | |
}); | |
$zoomIn.click(function() { | |
$mainImage.cropper("zoom", 0.1); | |
$mainImage.trigger('cropend'); | |
}); | |
$zoomOut.click(function() { | |
$mainImage.cropper("zoom", -0.1); | |
$mainImage.trigger('cropend'); | |
}); | |
$reset.click(function() { | |
$mainImage.cropper("reset"); | |
$mainImage.trigger('cropend'); | |
}); | |
} | |
element.on('CrudField:disable', function(e) { | |
element.children('.btn-group').children('button[data-handle=remove]').attr('disabled','disabled'); | |
element.children('.btn-group').children('.btn-file').children('input[data-handle=uploadImage]').attr('disabled','disabled'); | |
}); | |
element.on('CrudField:enable', function(e) { | |
element.children('.btn-group').children('.btn-file').children('input[data-handle=uploadImage]').removeAttr('disabled'); | |
element.children('.btn-group').children('button[data-handle=remove]').removeAttr('disabled'); | |
}); | |
} | |
</script> | |
@endBassetBlock | |
@endpush | |
{{-- End of Extra CSS and JS --}} | |
{{-- ########################################## --}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CRUD::field([ | |
'name' => 'photo', | |
'label' => 'Photo', | |
'type' => 'webcam', | |
'filename' => null, | |
'crop' => true, | |
'aspect_ratio' => 0.75, | |
]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment