-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[No QA] [TS migration] Migrate 'cropOrRotateImage' lib to TypeScript #27419
Changes from 3 commits
060876a
5c15ebb
0cc9181
fd3e146
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,18 @@ | ||||||||||||||||||
import RNImageManipulator from '@oguzhnatly/react-native-image-manipulator'; | ||||||||||||||||||
import RNFetchBlob from 'react-native-blob-util'; | ||||||||||||||||||
import {CropOrRotateImage} from './types'; | ||||||||||||||||||
|
||||||||||||||||||
/** | ||||||||||||||||||
* Crops and rotates the image on ios/android | ||||||||||||||||||
*/ | ||||||||||||||||||
const cropOrRotateImage: CropOrRotateImage = (uri, actions, options) => | ||||||||||||||||||
new Promise((resolve) => { | ||||||||||||||||||
RNImageManipulator.manipulate(uri, actions, options).then((result) => { | ||||||||||||||||||
RNFetchBlob.fs.stat(result.uri.replace('file://', '')).then(({size}) => { | ||||||||||||||||||
const file = Object.assign(result, {size, type: options.type || 'image/jpeg', name: options.name || 'fileName.jpg'}); | ||||||||||||||||||
resolve(file); | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
The old way still works, isn't? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, fixed @fabioh8010 |
||||||||||||||||||
}); | ||||||||||||||||||
}); | ||||||||||||||||||
}); | ||||||||||||||||||
|
||||||||||||||||||
export default cropOrRotateImage; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,14 @@ | ||
import _ from 'underscore'; | ||
import {CropOptions, CropOrRotateImage, CropOrRotateImageOptions, FileWithUri} from './types'; | ||
|
||
type SizeFromAngle = { | ||
width: number; | ||
height: number; | ||
}; | ||
|
||
/** | ||
* Calculates a size of canvas after rotation | ||
* | ||
* @param {Number} width | ||
* @param {Number} height | ||
* @param {Number} angle | ||
* @returns {Object} Returns width and height of new canvas | ||
*/ | ||
function sizeFromAngle(width, height, angle) { | ||
function sizeFromAngle(width: number, height: number, angle: number): SizeFromAngle { | ||
const radians = (angle * Math.PI) / 180; | ||
let sine = Math.cos(radians); | ||
let cosine = Math.sin(radians); | ||
|
@@ -23,12 +23,8 @@ function sizeFromAngle(width, height, angle) { | |
|
||
/** | ||
* Creates a new rotated canvas | ||
* | ||
* @param {Object} canvas | ||
* @param {Number} degrees | ||
* @returns {Object} | ||
*/ | ||
function rotateCanvas(canvas, degrees) { | ||
function rotateCanvas(canvas: HTMLCanvasElement, degrees: number): HTMLCanvasElement { | ||
const {width, height} = sizeFromAngle(canvas.width, canvas.height, degrees); | ||
|
||
// We have to create a new canvas because it is not possible to change already drawn | ||
|
@@ -38,28 +34,24 @@ function rotateCanvas(canvas, degrees) { | |
result.height = height; | ||
|
||
const context = result.getContext('2d'); | ||
if (context) { | ||
// In order to rotate image along its center we have to apply next transformation | ||
context.translate(result.width / 2, result.height / 2); | ||
|
||
// In order to rotate image along its center we have to apply next transformation | ||
context.translate(result.width / 2, result.height / 2); | ||
|
||
const radians = (degrees * Math.PI) / 180; | ||
context.rotate(radians); | ||
|
||
context.drawImage(canvas, -canvas.width / 2, -canvas.height / 2, canvas.width, canvas.height); | ||
const radians = (degrees * Math.PI) / 180; | ||
context.rotate(radians); | ||
|
||
context.drawImage(canvas, -canvas.width / 2, -canvas.height / 2, canvas.width, canvas.height); | ||
} | ||
return result; | ||
} | ||
|
||
/** | ||
* Creates new cropped canvas and returns it | ||
* | ||
* @param {Object} canvas | ||
* @param {Object} options | ||
* @returns {Object} | ||
*/ | ||
function cropCanvas(canvas, options) { | ||
function cropCanvas(canvas: HTMLCanvasElement, options: CropOptions) { | ||
let {originX = 0, originY = 0, width = 0, height = 0} = options; | ||
const clamp = (value, max) => Math.max(0, Math.min(max, value)); | ||
const clamp = (value: number, max: number) => Math.max(0, Math.min(max, value)); | ||
|
||
width = clamp(width, canvas.width); | ||
height = clamp(height, canvas.height); | ||
|
@@ -74,33 +66,29 @@ function cropCanvas(canvas, options) { | |
result.height = height; | ||
|
||
const context = result.getContext('2d'); | ||
context.drawImage(canvas, originX, originY, width, height, 0, 0, width, height); | ||
context?.drawImage(canvas, originX, originY, width, height, 0, 0, width, height); | ||
|
||
return result; | ||
} | ||
|
||
/** | ||
* @param {Object} canvas | ||
* @param {Object} options | ||
* @returns {Promise<File>} | ||
*/ | ||
function convertCanvasToFile(canvas, options = {}) { | ||
function convertCanvasToFile(canvas: HTMLCanvasElement, options: CropOrRotateImageOptions): Promise<FileWithUri> { | ||
return new Promise((resolve) => { | ||
canvas.toBlob((blob) => { | ||
if (!blob) { | ||
return; | ||
} | ||
const file = new File([blob], options.name || 'fileName.jpeg', {type: options.type || 'image/jpeg'}); | ||
file.uri = URL.createObjectURL(file); | ||
resolve(file); | ||
const fileWithUri = Object.assign(file, {uri: URL.createObjectURL(file)}); | ||
|
||
resolve(fileWithUri); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. const file = new File([blob], options.name || 'fileName.jpeg', {type: options.type || 'image/jpeg'}) as FileWithUri;
file.uri = URL.createObjectURL(file);
resolve(file); In this way you don't need to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed @fabioh8010 |
||
}); | ||
}); | ||
} | ||
|
||
/** | ||
* Loads image from specified url | ||
* | ||
* @param {String} uri | ||
* @returns {Promise<Object>} | ||
*/ | ||
function loadImageAsync(uri) { | ||
function loadImageAsync(uri: string): Promise<HTMLCanvasElement> { | ||
return new Promise((resolve, reject) => { | ||
const imageSource = new Image(); | ||
imageSource.crossOrigin = 'anonymous'; | ||
|
@@ -110,7 +98,7 @@ function loadImageAsync(uri) { | |
canvas.height = imageSource.naturalHeight; | ||
|
||
const context = canvas.getContext('2d'); | ||
context.drawImage(imageSource, 0, 0, imageSource.naturalWidth, imageSource.naturalHeight); | ||
context?.drawImage(imageSource, 0, 0, imageSource.naturalWidth, imageSource.naturalHeight); | ||
|
||
resolve(canvas); | ||
}; | ||
|
@@ -121,30 +109,21 @@ function loadImageAsync(uri) { | |
|
||
/** | ||
* Crops and rotates the image on web | ||
* | ||
* @param {String} uri | ||
* @param {Object} actions | ||
* @param {Object} options | ||
* @returns {Promise<Object>} Returns cropped and rotated image | ||
*/ | ||
function cropOrRotateImage(uri, actions, options) { | ||
return loadImageAsync(uri).then((originalCanvas) => { | ||
const resultCanvas = _.reduce( | ||
actions, | ||
(canvas, action) => { | ||
if ('crop' in action) { | ||
return cropCanvas(canvas, action.crop); | ||
} | ||
if ('rotate' in action) { | ||
return rotateCanvas(canvas, action.rotate); | ||
} | ||
return canvas; | ||
}, | ||
originalCanvas, | ||
); | ||
|
||
kubabutkiewicz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const cropOrRotateImage: CropOrRotateImage = (uri, actions, options) => | ||
kubabutkiewicz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
loadImageAsync(uri).then((originalCanvas) => { | ||
const resultCanvas = actions.reduce((canvas, action) => { | ||
if (action.crop) { | ||
return cropCanvas(canvas, action.crop); | ||
} | ||
if (action.rotate) { | ||
return rotateCanvas(canvas, action.rotate); | ||
} | ||
return canvas; | ||
}, originalCanvas); | ||
|
||
return convertCanvasToFile(resultCanvas, options); | ||
}); | ||
} | ||
|
||
export default cropOrRotateImage; |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,26 @@ | ||||||
import {RNImageManipulatorResult} from '@oguzhnatly/react-native-image-manipulator'; | ||||||
|
||||||
type CropOrRotateImageOptions = { | ||||||
type: string; | ||||||
name: string; | ||||||
compress: number; | ||||||
}; | ||||||
type CropOptions = { | ||||||
kubabutkiewicz marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
originX: number; | ||||||
originY: number; | ||||||
width: number; | ||||||
height: number; | ||||||
}; | ||||||
type Action = { | ||||||
kubabutkiewicz marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
crop?: CropOptions; | ||||||
rotate?: number; | ||||||
}; | ||||||
type FileWithUri = File & { | ||||||
kubabutkiewicz marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
uri: string; | ||||||
}; | ||||||
|
||||||
type RNManipulator = RNImageManipulatorResult & {size: number; type: string; name: string}; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed @fabioh8010 |
||||||
|
||||||
type CropOrRotateImage = (uri: string, actions: Action[], options: CropOrRotateImageOptions) => Promise<FileWithUri | RNManipulator>; | ||||||
|
||||||
export type {CropOrRotateImage, CropOptions, Action, FileWithUri, CropOrRotateImageOptions}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems a little weird to me. Any reason not to declare the types inline?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@thiagobrez when we have platform specific files we need to follow this pattern. Thats why its like that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kubabutkiewicz Cool! 👍🏻