load-image.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. /*
  2. * JavaScript Load Image
  3. * https://github.com/blueimp/JavaScript-Load-Image
  4. *
  5. * Copyright 2011, Sebastian Tschan
  6. * https://blueimp.net
  7. *
  8. * Licensed under the MIT license:
  9. * https://opensource.org/licenses/MIT
  10. */
  11. /* global define, URL, webkitURL, FileReader */
  12. ;(function ($) {
  13. 'use strict'
  14. // Loads an image for a given File object.
  15. // Invokes the callback with an img or optional canvas
  16. // element (if supported by the browser) as parameter:
  17. function loadImage (file, callback, options) {
  18. var img = document.createElement('img')
  19. var url
  20. img.onerror = function (event) {
  21. return loadImage.onerror(img, event, file, callback, options)
  22. }
  23. img.onload = function (event) {
  24. return loadImage.onload(img, event, file, callback, options)
  25. }
  26. if (loadImage.isInstanceOf('Blob', file) ||
  27. // Files are also Blob instances, but some browsers
  28. // (Firefox 3.6) support the File API but not Blobs:
  29. loadImage.isInstanceOf('File', file)) {
  30. url = img._objectURL = loadImage.createObjectURL(file)
  31. } else if (typeof file === 'string') {
  32. url = file
  33. if (options && options.crossOrigin) {
  34. img.crossOrigin = options.crossOrigin
  35. }
  36. } else {
  37. return false
  38. }
  39. if (url) {
  40. img.src = url
  41. return img
  42. }
  43. return loadImage.readFile(file, function (e) {
  44. var target = e.target
  45. if (target && target.result) {
  46. img.src = target.result
  47. } else if (callback) {
  48. callback(e)
  49. }
  50. })
  51. }
  52. // The check for URL.revokeObjectURL fixes an issue with Opera 12,
  53. // which provides URL.createObjectURL but doesn't properly implement it:
  54. var urlAPI = (window.createObjectURL && window) ||
  55. (window.URL && URL.revokeObjectURL && URL) ||
  56. (window.webkitURL && webkitURL)
  57. function revokeHelper (img, options) {
  58. if (img._objectURL && !(options && options.noRevoke)) {
  59. loadImage.revokeObjectURL(img._objectURL)
  60. delete img._objectURL
  61. }
  62. }
  63. loadImage.isInstanceOf = function (type, obj) {
  64. // Cross-frame instanceof check
  65. return Object.prototype.toString.call(obj) === '[object ' + type + ']'
  66. }
  67. loadImage.transform = function (img, options, callback, file, data) {
  68. callback(loadImage.scale(img, options, data), data)
  69. }
  70. loadImage.onerror = function (img, event, file, callback, options) {
  71. revokeHelper(img, options)
  72. if (callback) {
  73. callback.call(img, event)
  74. }
  75. }
  76. loadImage.onload = function (img, event, file, callback, options) {
  77. revokeHelper(img, options)
  78. if (callback) {
  79. loadImage.transform(img, options, callback, file, {})
  80. }
  81. }
  82. // Transform image coordinates, allows to override e.g.
  83. // the canvas orientation based on the orientation option,
  84. // gets canvas, options passed as arguments:
  85. loadImage.transformCoordinates = function () {
  86. return
  87. }
  88. // Returns transformed options, allows to override e.g.
  89. // maxWidth, maxHeight and crop options based on the aspectRatio.
  90. // gets img, options passed as arguments:
  91. loadImage.getTransformedOptions = function (img, options) {
  92. var aspectRatio = options.aspectRatio
  93. var newOptions
  94. var i
  95. var width
  96. var height
  97. if (!aspectRatio) {
  98. return options
  99. }
  100. newOptions = {}
  101. for (i in options) {
  102. if (options.hasOwnProperty(i)) {
  103. newOptions[i] = options[i]
  104. }
  105. }
  106. newOptions.crop = true
  107. width = img.naturalWidth || img.width
  108. height = img.naturalHeight || img.height
  109. if (width / height > aspectRatio) {
  110. newOptions.maxWidth = height * aspectRatio
  111. newOptions.maxHeight = height
  112. } else {
  113. newOptions.maxWidth = width
  114. newOptions.maxHeight = width / aspectRatio
  115. }
  116. return newOptions
  117. }
  118. // Canvas render method, allows to implement a different rendering algorithm:
  119. loadImage.renderImageToCanvas = function (
  120. canvas,
  121. img,
  122. sourceX,
  123. sourceY,
  124. sourceWidth,
  125. sourceHeight,
  126. destX,
  127. destY,
  128. destWidth,
  129. destHeight
  130. ) {
  131. canvas.getContext('2d').drawImage(
  132. img,
  133. sourceX,
  134. sourceY,
  135. sourceWidth,
  136. sourceHeight,
  137. destX,
  138. destY,
  139. destWidth,
  140. destHeight
  141. )
  142. return canvas
  143. }
  144. // Determines if the target image should be a canvas element:
  145. loadImage.hasCanvasOption = function (options) {
  146. return options.canvas || options.crop || !!options.aspectRatio
  147. }
  148. // Scales and/or crops the given image (img or canvas HTML element)
  149. // using the given options.
  150. // Returns a canvas object if the browser supports canvas
  151. // and the hasCanvasOption method returns true or a canvas
  152. // object is passed as image, else the scaled image:
  153. loadImage.scale = function (img, options, data) {
  154. options = options || {}
  155. var canvas = document.createElement('canvas')
  156. var useCanvas = img.getContext ||
  157. (loadImage.hasCanvasOption(options) && canvas.getContext)
  158. var width = img.naturalWidth || img.width
  159. var height = img.naturalHeight || img.height
  160. var destWidth = width
  161. var destHeight = height
  162. var maxWidth
  163. var maxHeight
  164. var minWidth
  165. var minHeight
  166. var sourceWidth
  167. var sourceHeight
  168. var sourceX
  169. var sourceY
  170. var pixelRatio
  171. var downsamplingRatio
  172. var tmp
  173. function scaleUp () {
  174. var scale = Math.max(
  175. (minWidth || destWidth) / destWidth,
  176. (minHeight || destHeight) / destHeight
  177. )
  178. if (scale > 1) {
  179. destWidth *= scale
  180. destHeight *= scale
  181. }
  182. }
  183. function scaleDown () {
  184. var scale = Math.min(
  185. (maxWidth || destWidth) / destWidth,
  186. (maxHeight || destHeight) / destHeight
  187. )
  188. if (scale < 1) {
  189. destWidth *= scale
  190. destHeight *= scale
  191. }
  192. }
  193. if (useCanvas) {
  194. options = loadImage.getTransformedOptions(img, options, data)
  195. sourceX = options.left || 0
  196. sourceY = options.top || 0
  197. if (options.sourceWidth) {
  198. sourceWidth = options.sourceWidth
  199. if (options.right !== undefined && options.left === undefined) {
  200. sourceX = width - sourceWidth - options.right
  201. }
  202. } else {
  203. sourceWidth = width - sourceX - (options.right || 0)
  204. }
  205. if (options.sourceHeight) {
  206. sourceHeight = options.sourceHeight
  207. if (options.bottom !== undefined && options.top === undefined) {
  208. sourceY = height - sourceHeight - options.bottom
  209. }
  210. } else {
  211. sourceHeight = height - sourceY - (options.bottom || 0)
  212. }
  213. destWidth = sourceWidth
  214. destHeight = sourceHeight
  215. }
  216. maxWidth = options.maxWidth
  217. maxHeight = options.maxHeight
  218. minWidth = options.minWidth
  219. minHeight = options.minHeight
  220. if (useCanvas && maxWidth && maxHeight && options.crop) {
  221. destWidth = maxWidth
  222. destHeight = maxHeight
  223. tmp = sourceWidth / sourceHeight - maxWidth / maxHeight
  224. if (tmp < 0) {
  225. sourceHeight = maxHeight * sourceWidth / maxWidth
  226. if (options.top === undefined && options.bottom === undefined) {
  227. sourceY = (height - sourceHeight) / 2
  228. }
  229. } else if (tmp > 0) {
  230. sourceWidth = maxWidth * sourceHeight / maxHeight
  231. if (options.left === undefined && options.right === undefined) {
  232. sourceX = (width - sourceWidth) / 2
  233. }
  234. }
  235. } else {
  236. if (options.contain || options.cover) {
  237. minWidth = maxWidth = maxWidth || minWidth
  238. minHeight = maxHeight = maxHeight || minHeight
  239. }
  240. if (options.cover) {
  241. scaleDown()
  242. scaleUp()
  243. } else {
  244. scaleUp()
  245. scaleDown()
  246. }
  247. }
  248. if (useCanvas) {
  249. pixelRatio = options.pixelRatio
  250. if (pixelRatio > 1) {
  251. canvas.style.width = destWidth + 'px'
  252. canvas.style.height = destHeight + 'px'
  253. destWidth *= pixelRatio
  254. destHeight *= pixelRatio
  255. canvas.getContext('2d').scale(pixelRatio, pixelRatio)
  256. }
  257. downsamplingRatio = options.downsamplingRatio
  258. if (downsamplingRatio > 0 && downsamplingRatio < 1 &&
  259. destWidth < sourceWidth && destHeight < sourceHeight) {
  260. while (sourceWidth * downsamplingRatio > destWidth) {
  261. canvas.width = sourceWidth * downsamplingRatio
  262. canvas.height = sourceHeight * downsamplingRatio
  263. loadImage.renderImageToCanvas(
  264. canvas,
  265. img,
  266. sourceX,
  267. sourceY,
  268. sourceWidth,
  269. sourceHeight,
  270. 0,
  271. 0,
  272. canvas.width,
  273. canvas.height
  274. )
  275. sourceX = 0
  276. sourceY = 0
  277. sourceWidth = canvas.width
  278. sourceHeight = canvas.height
  279. img = document.createElement('canvas')
  280. img.width = sourceWidth
  281. img.height = sourceHeight
  282. loadImage.renderImageToCanvas(
  283. img,
  284. canvas,
  285. 0,
  286. 0,
  287. sourceWidth,
  288. sourceHeight,
  289. 0,
  290. 0,
  291. sourceWidth,
  292. sourceHeight
  293. )
  294. }
  295. }
  296. canvas.width = destWidth
  297. canvas.height = destHeight
  298. loadImage.transformCoordinates(
  299. canvas,
  300. options
  301. )
  302. return loadImage.renderImageToCanvas(
  303. canvas,
  304. img,
  305. sourceX,
  306. sourceY,
  307. sourceWidth,
  308. sourceHeight,
  309. 0,
  310. 0,
  311. destWidth,
  312. destHeight
  313. )
  314. }
  315. img.width = destWidth
  316. img.height = destHeight
  317. return img
  318. }
  319. loadImage.createObjectURL = function (file) {
  320. return urlAPI ? urlAPI.createObjectURL(file) : false
  321. }
  322. loadImage.revokeObjectURL = function (url) {
  323. return urlAPI ? urlAPI.revokeObjectURL(url) : false
  324. }
  325. // Loads a given File object via FileReader interface,
  326. // invokes the callback with the event object (load or error).
  327. // The result can be read via event.target.result:
  328. loadImage.readFile = function (file, callback, method) {
  329. if (window.FileReader) {
  330. var fileReader = new FileReader()
  331. fileReader.onload = fileReader.onerror = callback
  332. method = method || 'readAsDataURL'
  333. if (fileReader[method]) {
  334. fileReader[method](file)
  335. return fileReader
  336. }
  337. }
  338. return false
  339. }
  340. if (typeof define === 'function' && define.amd) {
  341. define(function () {
  342. return loadImage
  343. })
  344. } else if (typeof module === 'object' && module.exports) {
  345. module.exports = loadImage
  346. } else {
  347. $.loadImage = loadImage
  348. }
  349. }(window))