Commit 8f7acb3f authored by kang.nie@inzymeits.com's avatar kang.nie@inzymeits.com
Browse files

初始化代码

parent 2d7d3f82
Pipeline #3104 failed with stages
in 0 seconds
/* eslint-disable */
var debug = false;
var EXIF = function(obj) {
if (obj instanceof EXIF) return obj;
if (!(this instanceof EXIF)) return new EXIF(obj);
this.EXIFwrapped = obj;
};
var ExifTags = EXIF.Tags = {
// version tags
0x9000 : "ExifVersion", // EXIF version
0xA000 : "FlashpixVersion", // Flashpix format version
// colorspace tags
0xA001 : "ColorSpace", // Color space information tag
// image configuration
0xA002 : "PixelXDimension", // Valid width of meaningful image
0xA003 : "PixelYDimension", // Valid height of meaningful image
0x9101 : "ComponentsConfiguration", // Information about channels
0x9102 : "CompressedBitsPerPixel", // Compressed bits per pixel
// user information
0x927C : "MakerNote", // Any desired information written by the manufacturer
0x9286 : "UserComment", // Comments by user
// related file
0xA004 : "RelatedSoundFile", // Name of related sound file
// date and time
0x9003 : "DateTimeOriginal", // Date and time when the original image was generated
0x9004 : "DateTimeDigitized", // Date and time when the image was stored digitally
0x9290 : "SubsecTime", // Fractions of seconds for DateTime
0x9291 : "SubsecTimeOriginal", // Fractions of seconds for DateTimeOriginal
0x9292 : "SubsecTimeDigitized", // Fractions of seconds for DateTimeDigitized
// picture-taking conditions
0x829A : "ExposureTime", // Exposure time (in seconds)
0x829D : "FNumber", // F number
0x8822 : "ExposureProgram", // Exposure program
0x8824 : "SpectralSensitivity", // Spectral sensitivity
0x8827 : "ISOSpeedRatings", // ISO speed rating
0x8828 : "OECF", // Optoelectric conversion factor
0x9201 : "ShutterSpeedValue", // Shutter speed
0x9202 : "ApertureValue", // Lens aperture
0x9203 : "BrightnessValue", // Value of brightness
0x9204 : "ExposureBias", // Exposure bias
0x9205 : "MaxApertureValue", // Smallest F number of lens
0x9206 : "SubjectDistance", // Distance to subject in meters
0x9207 : "MeteringMode", // Metering mode
0x9208 : "LightSource", // Kind of light source
0x9209 : "Flash", // Flash status
0x9214 : "SubjectArea", // Location and area of main subject
0x920A : "FocalLength", // Focal length of the lens in mm
0xA20B : "FlashEnergy", // Strobe energy in BCPS
0xA20C : "SpatialFrequencyResponse", //
0xA20E : "FocalPlaneXResolution", // Number of pixels in width direction per FocalPlaneResolutionUnit
0xA20F : "FocalPlaneYResolution", // Number of pixels in height direction per FocalPlaneResolutionUnit
0xA210 : "FocalPlaneResolutionUnit", // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution
0xA214 : "SubjectLocation", // Location of subject in image
0xA215 : "ExposureIndex", // Exposure index selected on camera
0xA217 : "SensingMethod", // Image sensor type
0xA300 : "FileSource", // Image source (3 == DSC)
0xA301 : "SceneType", // Scene type (1 == directly photographed)
0xA302 : "CFAPattern", // Color filter array geometric pattern
0xA401 : "CustomRendered", // Special processing
0xA402 : "ExposureMode", // Exposure mode
0xA403 : "WhiteBalance", // 1 = auto white balance, 2 = manual
0xA404 : "DigitalZoomRation", // Digital zoom ratio
0xA405 : "FocalLengthIn35mmFilm", // Equivalent foacl length assuming 35mm film camera (in mm)
0xA406 : "SceneCaptureType", // Type of scene
0xA407 : "GainControl", // Degree of overall image gain adjustment
0xA408 : "Contrast", // Direction of contrast processing applied by camera
0xA409 : "Saturation", // Direction of saturation processing applied by camera
0xA40A : "Sharpness", // Direction of sharpness processing applied by camera
0xA40B : "DeviceSettingDescription", //
0xA40C : "SubjectDistanceRange", // Distance to subject
// other tags
0xA005 : "InteroperabilityIFDPointer",
0xA420 : "ImageUniqueID" // Identifier assigned uniquely to each image
};
var TiffTags = EXIF.TiffTags = {
0x0100 : "ImageWidth",
0x0101 : "ImageHeight",
0x8769 : "ExifIFDPointer",
0x8825 : "GPSInfoIFDPointer",
0xA005 : "InteroperabilityIFDPointer",
0x0102 : "BitsPerSample",
0x0103 : "Compression",
0x0106 : "PhotometricInterpretation",
0x0112 : "Orientation",
0x0115 : "SamplesPerPixel",
0x011C : "PlanarConfiguration",
0x0212 : "YCbCrSubSampling",
0x0213 : "YCbCrPositioning",
0x011A : "XResolution",
0x011B : "YResolution",
0x0128 : "ResolutionUnit",
0x0111 : "StripOffsets",
0x0116 : "RowsPerStrip",
0x0117 : "StripByteCounts",
0x0201 : "JPEGInterchangeFormat",
0x0202 : "JPEGInterchangeFormatLength",
0x012D : "TransferFunction",
0x013E : "WhitePoint",
0x013F : "PrimaryChromaticities",
0x0211 : "YCbCrCoefficients",
0x0214 : "ReferenceBlackWhite",
0x0132 : "DateTime",
0x010E : "ImageDescription",
0x010F : "Make",
0x0110 : "Model",
0x0131 : "Software",
0x013B : "Artist",
0x8298 : "Copyright"
};
var GPSTags = EXIF.GPSTags = {
0x0000 : "GPSVersionID",
0x0001 : "GPSLatitudeRef",
0x0002 : "GPSLatitude",
0x0003 : "GPSLongitudeRef",
0x0004 : "GPSLongitude",
0x0005 : "GPSAltitudeRef",
0x0006 : "GPSAltitude",
0x0007 : "GPSTimeStamp",
0x0008 : "GPSSatellites",
0x0009 : "GPSStatus",
0x000A : "GPSMeasureMode",
0x000B : "GPSDOP",
0x000C : "GPSSpeedRef",
0x000D : "GPSSpeed",
0x000E : "GPSTrackRef",
0x000F : "GPSTrack",
0x0010 : "GPSImgDirectionRef",
0x0011 : "GPSImgDirection",
0x0012 : "GPSMapDatum",
0x0013 : "GPSDestLatitudeRef",
0x0014 : "GPSDestLatitude",
0x0015 : "GPSDestLongitudeRef",
0x0016 : "GPSDestLongitude",
0x0017 : "GPSDestBearingRef",
0x0018 : "GPSDestBearing",
0x0019 : "GPSDestDistanceRef",
0x001A : "GPSDestDistance",
0x001B : "GPSProcessingMethod",
0x001C : "GPSAreaInformation",
0x001D : "GPSDateStamp",
0x001E : "GPSDifferential"
};
var StringValues = EXIF.StringValues = {
ExposureProgram : {
0 : "Not defined",
1 : "Manual",
2 : "Normal program",
3 : "Aperture priority",
4 : "Shutter priority",
5 : "Creative program",
6 : "Action program",
7 : "Portrait mode",
8 : "Landscape mode"
},
MeteringMode : {
0 : "Unknown",
1 : "Average",
2 : "CenterWeightedAverage",
3 : "Spot",
4 : "MultiSpot",
5 : "Pattern",
6 : "Partial",
255 : "Other"
},
LightSource : {
0 : "Unknown",
1 : "Daylight",
2 : "Fluorescent",
3 : "Tungsten (incandescent light)",
4 : "Flash",
9 : "Fine weather",
10 : "Cloudy weather",
11 : "Shade",
12 : "Daylight fluorescent (D 5700 - 7100K)",
13 : "Day white fluorescent (N 4600 - 5400K)",
14 : "Cool white fluorescent (W 3900 - 4500K)",
15 : "White fluorescent (WW 3200 - 3700K)",
17 : "Standard light A",
18 : "Standard light B",
19 : "Standard light C",
20 : "D55",
21 : "D65",
22 : "D75",
23 : "D50",
24 : "ISO studio tungsten",
255 : "Other"
},
Flash : {
0x0000 : "Flash did not fire",
0x0001 : "Flash fired",
0x0005 : "Strobe return light not detected",
0x0007 : "Strobe return light detected",
0x0009 : "Flash fired, compulsory flash mode",
0x000D : "Flash fired, compulsory flash mode, return light not detected",
0x000F : "Flash fired, compulsory flash mode, return light detected",
0x0010 : "Flash did not fire, compulsory flash mode",
0x0018 : "Flash did not fire, auto mode",
0x0019 : "Flash fired, auto mode",
0x001D : "Flash fired, auto mode, return light not detected",
0x001F : "Flash fired, auto mode, return light detected",
0x0020 : "No flash function",
0x0041 : "Flash fired, red-eye reduction mode",
0x0045 : "Flash fired, red-eye reduction mode, return light not detected",
0x0047 : "Flash fired, red-eye reduction mode, return light detected",
0x0049 : "Flash fired, compulsory flash mode, red-eye reduction mode",
0x004D : "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",
0x004F : "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",
0x0059 : "Flash fired, auto mode, red-eye reduction mode",
0x005D : "Flash fired, auto mode, return light not detected, red-eye reduction mode",
0x005F : "Flash fired, auto mode, return light detected, red-eye reduction mode"
},
SensingMethod : {
1 : "Not defined",
2 : "One-chip color area sensor",
3 : "Two-chip color area sensor",
4 : "Three-chip color area sensor",
5 : "Color sequential area sensor",
7 : "Trilinear sensor",
8 : "Color sequential linear sensor"
},
SceneCaptureType : {
0 : "Standard",
1 : "Landscape",
2 : "Portrait",
3 : "Night scene"
},
SceneType : {
1 : "Directly photographed"
},
CustomRendered : {
0 : "Normal process",
1 : "Custom process"
},
WhiteBalance : {
0 : "Auto white balance",
1 : "Manual white balance"
},
GainControl : {
0 : "None",
1 : "Low gain up",
2 : "High gain up",
3 : "Low gain down",
4 : "High gain down"
},
Contrast : {
0 : "Normal",
1 : "Soft",
2 : "Hard"
},
Saturation : {
0 : "Normal",
1 : "Low saturation",
2 : "High saturation"
},
Sharpness : {
0 : "Normal",
1 : "Soft",
2 : "Hard"
},
SubjectDistanceRange : {
0 : "Unknown",
1 : "Macro",
2 : "Close view",
3 : "Distant view"
},
FileSource : {
3 : "DSC"
},
Components : {
0 : "",
1 : "Y",
2 : "Cb",
3 : "Cr",
4 : "R",
5 : "G",
6 : "B"
}
};
function addEvent(element, event, handler) {
if (element.addEventListener) {
element.addEventListener(event, handler, false);
} else if (element.attachEvent) {
element.attachEvent("on" + event, handler);
}
}
function imageHasData(img) {
return !!(img.exifdata);
}
function base64ToArrayBuffer(base64, contentType) {
contentType = contentType || base64.match(/^data\:([^\;]+)\;base64,/mi)[1] || ''; // e.g. 'data:image/jpeg;base64,...' => 'image/jpeg'
base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, '');
var binary = atob(base64);
var len = binary.length;
var buffer = new ArrayBuffer(len);
var view = new Uint8Array(buffer);
for (var i = 0; i < len; i++) {
view[i] = binary.charCodeAt(i);
}
return buffer;
}
function objectURLToBlob(url, callback) {
var http = new XMLHttpRequest();
http.open("GET", url, true);
http.responseType = "blob";
http.onload = function(e) {
if (this.status == 200 || this.status === 0) {
callback(this.response);
}
};
http.send();
}
function getImageData(img, callback) {
function handleBinaryFile(binFile) {
var data = findEXIFinJPEG(binFile);
var iptcdata = findIPTCinJPEG(binFile);
img.exifdata = data || {};
img.iptcdata = iptcdata || {};
if (callback) {
callback.call(img);
}
}
if (img.src) {
if (/^data\:/i.test(img.src)) { // Data URI
var arrayBuffer = base64ToArrayBuffer(img.src);
handleBinaryFile(arrayBuffer);
} else if (/^blob\:/i.test(img.src)) { // Object URL
var fileReader = new FileReader();
fileReader.onload = function(e) {
handleBinaryFile(e.target.result);
};
objectURLToBlob(img.src, function (blob) {
fileReader.readAsArrayBuffer(blob);
});
} else {
var http = new XMLHttpRequest();
http.onload = function() {
if (this.status == 200 || this.status === 0) {
handleBinaryFile(http.response);
} else {
throw "Could not load image";
}
http = null;
};
http.open("GET", img.src, true);
http.responseType = "arraybuffer";
http.send(null);
}
} else if (window.FileReader && (img instanceof window.Blob || img instanceof window.File)) {
var fileReader = new FileReader();
fileReader.onload = function(e) {
if (debug) console.log("Got file of length " + e.target.result.byteLength);
handleBinaryFile(e.target.result);
};
fileReader.readAsArrayBuffer(img);
}
}
function findEXIFinJPEG(file) {
var dataView = new DataView(file);
if (debug) console.log("Got file of length " + file.byteLength);
if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
if (debug) console.log("Not a valid JPEG");
return false; // not a valid jpeg
}
var offset = 2,
length = file.byteLength,
marker;
while (offset < length) {
if (dataView.getUint8(offset) != 0xFF) {
if (debug) console.log("Not a valid marker at offset " + offset + ", found: " + dataView.getUint8(offset));
return false; // not a valid marker, something is wrong
}
marker = dataView.getUint8(offset + 1);
if (debug) console.log(marker);
// we could implement handling for other markers here,
// but we're only looking for 0xFFE1 for EXIF data
if (marker == 225) {
if (debug) console.log("Found 0xFFE1 marker");
return readEXIFData(dataView, offset + 4, dataView.getUint16(offset + 2) - 2);
// offset += 2 + file.getShortAt(offset+2, true);
} else {
offset += 2 + dataView.getUint16(offset+2);
}
}
}
function findIPTCinJPEG(file) {
var dataView = new DataView(file);
if (debug) console.log("Got file of length " + file.byteLength);
if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
if (debug) console.log("Not a valid JPEG");
return false; // not a valid jpeg
}
var offset = 2,
length = file.byteLength;
var isFieldSegmentStart = function(dataView, offset){
return (
dataView.getUint8(offset) === 0x38 &&
dataView.getUint8(offset+1) === 0x42 &&
dataView.getUint8(offset+2) === 0x49 &&
dataView.getUint8(offset+3) === 0x4D &&
dataView.getUint8(offset+4) === 0x04 &&
dataView.getUint8(offset+5) === 0x04
);
};
while (offset < length) {
if ( isFieldSegmentStart(dataView, offset )){
// Get the length of the name header (which is padded to an even number of bytes)
var nameHeaderLength = dataView.getUint8(offset+7);
if(nameHeaderLength % 2 !== 0) nameHeaderLength += 1;
// Check for pre photoshop 6 format
if(nameHeaderLength === 0) {
// Always 4
nameHeaderLength = 4;
}
var startOffset = offset + 8 + nameHeaderLength;
var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength);
return readIPTCData(file, startOffset, sectionLength);
break;
}
// Not the marker, continue searching
offset++;
}
}
var IptcFieldMap = {
0x78 : 'caption',
0x6E : 'credit',
0x19 : 'keywords',
0x37 : 'dateCreated',
0x50 : 'byline',
0x55 : 'bylineTitle',
0x7A : 'captionWriter',
0x69 : 'headline',
0x74 : 'copyright',
0x0F : 'category'
};
function readIPTCData(file, startOffset, sectionLength){
var dataView = new DataView(file);
var data = {};
var fieldValue, fieldName, dataSize, segmentType, segmentSize;
var segmentStartPos = startOffset;
while(segmentStartPos < startOffset+sectionLength) {
if(dataView.getUint8(segmentStartPos) === 0x1C && dataView.getUint8(segmentStartPos+1) === 0x02){
segmentType = dataView.getUint8(segmentStartPos+2);
if(segmentType in IptcFieldMap) {
dataSize = dataView.getInt16(segmentStartPos+3);
segmentSize = dataSize + 5;
fieldName = IptcFieldMap[segmentType];
fieldValue = getStringFromDB(dataView, segmentStartPos+5, dataSize);
// Check if we already stored a value with this name
if(data.hasOwnProperty(fieldName)) {
// Value already stored with this name, create multivalue field
if(data[fieldName] instanceof Array) {
data[fieldName].push(fieldValue);
}
else {
data[fieldName] = [data[fieldName], fieldValue];
}
}
else {
data[fieldName] = fieldValue;
}
}
}
segmentStartPos++;
}
return data;
}
function readTags(file, tiffStart, dirStart, strings, bigEnd) {
var entries = file.getUint16(dirStart, !bigEnd),
tags = {},
entryOffset, tag,
i;
for (i=0;i<entries;i++) {
entryOffset = dirStart + i*12 + 2;
tag = strings[file.getUint16(entryOffset, !bigEnd)];
if (!tag && debug) console.log("Unknown tag: " + file.getUint16(entryOffset, !bigEnd));
tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd);
}
return tags;
}
function readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd) {
var type = file.getUint16(entryOffset+2, !bigEnd),
numValues = file.getUint32(entryOffset+4, !bigEnd),
valueOffset = file.getUint32(entryOffset+8, !bigEnd) + tiffStart,
offset,
vals, val, n,
numerator, denominator;
switch (type) {
case 1: // byte, 8-bit unsigned int
case 7: // undefined, 8-bit byte, value depending on field
if (numValues == 1) {
return file.getUint8(entryOffset + 8, !bigEnd);
} else {
offset = numValues > 4 ? valueOffset : (entryOffset + 8);
vals = [];
for (n=0;n<numValues;n++) {
vals[n] = file.getUint8(offset + n);
}
return vals;
}
case 2: // ascii, 8-bit byte
offset = numValues > 4 ? valueOffset : (entryOffset + 8);
return getStringFromDB(file, offset, numValues-1);
case 3: // short, 16 bit int
if (numValues == 1) {
return file.getUint16(entryOffset + 8, !bigEnd);
} else {
offset = numValues > 2 ? valueOffset : (entryOffset + 8);
vals = [];
for (n=0;n<numValues;n++) {
vals[n] = file.getUint16(offset + 2*n, !bigEnd);
}
return vals;
}
case 4: // long, 32 bit int
if (numValues == 1) {
return file.getUint32(entryOffset + 8, !bigEnd);
} else {
vals = [];
for (n=0;n<numValues;n++) {
vals[n] = file.getUint32(valueOffset + 4*n, !bigEnd);
}
return vals;
}
case 5: // rational = two long values, first is numerator, second is denominator
if (numValues == 1) {
numerator = file.getUint32(valueOffset, !bigEnd);
denominator = file.getUint32(valueOffset+4, !bigEnd);
val = new Number(numerator / denominator);
val.numerator = numerator;
val.denominator = denominator;
return val;
} else {
vals = [];
for (n=0;n<numValues;n++) {
numerator = file.getUint32(valueOffset + 8*n, !bigEnd);
denominator = file.getUint32(valueOffset+4 + 8*n, !bigEnd);
vals[n] = new Number(numerator / denominator);
vals[n].numerator = numerator;
vals[n].denominator = denominator;
}
return vals;
}
case 9: // slong, 32 bit signed int
if (numValues == 1) {
return file.getInt32(entryOffset + 8, !bigEnd);
} else {
vals = [];
for (n=0;n<numValues;n++) {
vals[n] = file.getInt32(valueOffset + 4*n, !bigEnd);
}
return vals;
}
case 10: // signed rational, two slongs, first is numerator, second is denominator
if (numValues == 1) {
return file.getInt32(valueOffset, !bigEnd) / file.getInt32(valueOffset+4, !bigEnd);
} else {
vals = [];
for (n=0;n<numValues;n++) {
vals[n] = file.getInt32(valueOffset + 8*n, !bigEnd) / file.getInt32(valueOffset+4 + 8*n, !bigEnd);
}
return vals;
}
}
}
function getStringFromDB(buffer, start, length) {
var outstr = "";
for (var n = start; n < start+length; n++) {
outstr += String.fromCharCode(buffer.getUint8(n));
}
return outstr;
}
function readEXIFData(file, start) {
if (getStringFromDB(file, start, 4) != "Exif") {
if (debug) console.log("Not valid EXIF data! " + getStringFromDB(file, start, 4));
return false;
}
var bigEnd,
tags, tag,
exifData, gpsData,
tiffOffset = start + 6;
// test for TIFF validity and endianness
if (file.getUint16(tiffOffset) == 0x4949) {
bigEnd = false;
} else if (file.getUint16(tiffOffset) == 0x4D4D) {
bigEnd = true;
} else {
if (debug) console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)");
return false;
}
if (file.getUint16(tiffOffset+2, !bigEnd) != 0x002A) {
if (debug) console.log("Not valid TIFF data! (no 0x002A)");
return false;
}
var firstIFDOffset = file.getUint32(tiffOffset+4, !bigEnd);
if (firstIFDOffset < 0x00000008) {
if (debug) console.log("Not valid TIFF data! (First offset less than 8)", file.getUint32(tiffOffset+4, !bigEnd));
return false;
}
tags = readTags(file, tiffOffset, tiffOffset + firstIFDOffset, TiffTags, bigEnd);
if (tags.ExifIFDPointer) {
exifData = readTags(file, tiffOffset, tiffOffset + tags.ExifIFDPointer, ExifTags, bigEnd);
for (tag in exifData) {
switch (tag) {
case "LightSource" :
case "Flash" :
case "MeteringMode" :
case "ExposureProgram" :
case "SensingMethod" :
case "SceneCaptureType" :
case "SceneType" :
case "CustomRendered" :
case "WhiteBalance" :
case "GainControl" :
case "Contrast" :
case "Saturation" :
case "Sharpness" :
case "SubjectDistanceRange" :
case "FileSource" :
exifData[tag] = StringValues[tag][exifData[tag]];
break;
case "ExifVersion" :
case "FlashpixVersion" :
exifData[tag] = String.fromCharCode(exifData[tag][0], exifData[tag][1], exifData[tag][2], exifData[tag][3]);
break;
case "ComponentsConfiguration" :
exifData[tag] =
StringValues.Components[exifData[tag][0]] +
StringValues.Components[exifData[tag][1]] +
StringValues.Components[exifData[tag][2]] +
StringValues.Components[exifData[tag][3]];
break;
}
tags[tag] = exifData[tag];
}
}
if (tags.GPSInfoIFDPointer) {
gpsData = readTags(file, tiffOffset, tiffOffset + tags.GPSInfoIFDPointer, GPSTags, bigEnd);
for (tag in gpsData) {
switch (tag) {
case "GPSVersionID" :
gpsData[tag] = gpsData[tag][0] +
"." + gpsData[tag][1] +
"." + gpsData[tag][2] +
"." + gpsData[tag][3];
break;
}
tags[tag] = gpsData[tag];
}
}
return tags;
}
EXIF.getData = function(img, callback) {
if ((img instanceof Image || img instanceof HTMLImageElement) && !img.complete) return false;
if (!imageHasData(img)) {
getImageData(img, callback);
} else {
if (callback) {
callback.call(img);
}
}
return true;
}
EXIF.getTag = function(img, tag) {
if (!imageHasData(img)) return;
return img.exifdata[tag];
}
EXIF.getAllTags = function(img) {
if (!imageHasData(img)) return {};
var a,
data = img.exifdata,
tags = {};
for (a in data) {
if (data.hasOwnProperty(a)) {
tags[a] = data[a];
}
}
return tags;
}
EXIF.pretty = function(img) {
if (!imageHasData(img)) return "";
var a,
data = img.exifdata,
strPretty = "";
for (a in data) {
if (data.hasOwnProperty(a)) {
if (typeof data[a] == "object") {
if (data[a] instanceof Number) {
strPretty += a + " : " + data[a] + " [" + data[a].numerator + "/" + data[a].denominator + "]\r\n";
} else {
strPretty += a + " : [" + data[a].length + " values]\r\n";
}
} else {
strPretty += a + " : " + data[a] + "\r\n";
}
}
}
return strPretty;
}
EXIF.readFromBinaryFile = function(file) {
return findEXIFinJPEG(file);
}
export default EXIF
/* eslint-disable */
// 获取图片的 mimetype
// algorithm come from https://mimesniff.spec.whatwg.org/#matching-an-image-type-pattern
var IMAGES_PATTERNS = [
{
"bytePattern": [0x00, 0x00, 0x01, 0x00],
"patternMask": [0xff, 0xff, 0xff, 0xff],
"imageType": "image/x-icon"
},
{
"bytePattern": [0x00, 0x00, 0x02, 0x00],
"patternMask": [0xff, 0xff, 0xff, 0xff],
"imageType": "image/x-icon"
},
{
"bytePattern": [0x42, 0x4d],
"patternMask": [0xff, 0xff],
"imageType": "image/bmp"
},
{
"bytePattern": [0x47, 0x49, 0x46, 0x38, 0x37, 0x61],
"patternMask": [0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
"imageType": "image/gif"
},
{
"bytePattern": [0x47, 0x49, 0x46, 0x38, 0x39, 0x61],
"patternMask": [0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
"imageType": "image/gif"
},
{
"bytePattern": [0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50, 0x56, 0x50],
"patternMask": [0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
"imageType": "image/webp"
},
{
"bytePattern": [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],
"patternMask": [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
"imageType": "image/png"
},
{
"bytePattern": [0xff, 0xd8, 0xff],
"patternMask": [0xff, 0xff, 0xff],
"imageType": "image/jpeg"
}
];
function getImgMimeType(sequence) {
// sequence 为图片字节数组
var sequenceLen = sequence.length;
// 返回图片类型
var retType = null;
// 遍历图片模式,检测文件类型
for (var i = IMAGES_PATTERNS.length - 1; i >= 0; i--) {
// 当前文件模式
var cur = IMAGES_PATTERNS[i];
// 当前文件头模式
var bp = cur.bytePattern;
// 当前文件头模式掩码,用于确定文件签名字节
var pm = cur.patternMask;
var length = bp.length;
// 如果文件的字节数还没有模式的长度长,说明不是一个有效的该模式文件,继续检查下一个模式
if (sequenceLen < length) {
continue;
}
// 循环检查文件头的内容,确定文件类型
for (var j = 0; j < length; j++) {
// 只有当文件头字节序列中对应位置的掩码为 0xff 时,才确定是文件签名字节
if (pm[j] !== 0xff) {
continue;
}
// 判断当前文件头字节序列是否与指定模式相匹配
if (bp[j] !== sequence[j]) {
break;
}
}
if (j >= length) {
return (retType = cur.imageType);
}
}
return retType;
}
export default getImgMimeType;
/* eslint-disable */
// 基于 canvas 压缩图片为 jpg 格式
// 不支持裁剪,只做压缩
/*=============================================================================================*/
// 已发现并修复的 andriod bug:
// 1.调用 canvas.toDataURL('image/jpeg') 返回 image/png,导致图片变大
// 解决:
// 使用 JPEGEncode 生成 jpg
// 2.调用 FileReader.readDataAsURL() 没有返回 mime type,导致图片载入失败
// 解决:
// 侦测文件类型,将获取的 mime type 补进 base64
/*=============================================================================================*/
/*=============================================================================================*/
// 已发现并修复的 ios bug:
// ios bug 修复参考:https://github.com/stomita/ios-imagefile-megapixel
/*=============================================================================================*/
import JPEGEncoder from './jpegencoder';
import EXIF from './exif';
import blobutil from './blobutil';
import imagemime from './imagemime';
var ua = window.navigator.userAgent;
var android = /Android/.test(ua);
var ios = /(?:iPhone|iPod|iPad)/.test(ua);
/**
* Detect subsampling in loaded image.
* In iOS, larger images than 2M pixels may be subsampled in rendering.
*
* 用载入的图片检测二次抽样
* 在 ios 中,如果图片的像素数大于 2M 的话,渲染的时候可能会被二次抽样
*/
function detectSubsampling(img) {
var iw = img.naturalWidth || img.width;
var ih = img.naturalHeight || img.height;
if (iw * ih > 1024 * 1024) { // subsampling may happen over megapixel image
var canvas = document.createElement('canvas');
canvas.width = canvas.height = 1;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, -iw + 1, 0);
// subsampled image becomes half smaller in rendering size.
// check alpha channel value to confirm image is covering edge pixel or not.
// if alpha value is 0 image is not covering, hence subsampled.
// 渲染的时候,二次抽样后的图片会变成一半的大小。
// 检查 alpha 通道的值来确定图片是否覆盖了边缘像素。
// 如果 alpha 的值是 0 的话,说明图片没有覆盖边缘像素,于是可以确定图片已经被二次抽样了。
return ctx.getImageData(0, 0, 1, 1).data[3] === 0;
} else {
return false;
}
}
/**
* Detecting vertical squash in loaded image.
* Fixes a bug which squash image vertically while drawing into canvas for some images.
*
* 用载入的图片检测垂直压扁
* 修复了一个这样的BUG:当绘制一些图片到画布中时,图片会被垂直压扁
*/
function detectVerticalSquash(img, iw, ih) {
var canvas = document.createElement('canvas');
canvas.width = 1;
canvas.height = ih;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
var data = ctx.getImageData(0, 0, 1, ih).data;
// search image edge pixel position in case it is squashed vertically.
var sy = 0;
var ey = ih;
var py = ih;
while (py > sy) {
var alpha = data[(py - 1) * 4 + 3];
if (alpha === 0) {
ey = py;
} else {
sy = py;
}
py = (ey + sy) >> 1;
}
var ratio = (py / ih);
return (ratio === 0) ? 1 : ratio;
}
/**
* Transform canvas coordination according to specified frame size and orientation
* Orientation value is from EXIF tag
*/
function transformCoordinate(canvas, ctx, width, height, orientation) {
switch (orientation) {
case 5:
case 6:
case 7:
case 8:
canvas.width = height;
canvas.height = width;
break;
default:
canvas.width = width;
canvas.height = height;
}
switch (orientation) {
case 2:
// horizontal flip
ctx.translate(width, 0);
ctx.scale(-1, 1);
break;
case 3:
// 180 rotate left
ctx.translate(width, height);
ctx.rotate(Math.PI);
break;
case 4:
// vertical flip
ctx.translate(0, height);
ctx.scale(1, -1);
break;
case 5:
// vertical flip + 90 rotate right
ctx.rotate(0.5 * Math.PI);
ctx.scale(1, -1);
break;
case 6:
// 90 rotate right
ctx.rotate(0.5 * Math.PI);
ctx.translate(0, -height);
break;
case 7:
// horizontal flip + 90 rotate right
ctx.rotate(0.5 * Math.PI);
ctx.translate(width, -height);
ctx.scale(-1, 1);
break;
case 8:
// 90 rotate left
ctx.rotate(-0.5 * Math.PI);
ctx.translate(-width, 0);
break;
default:
break;
}
}
// 压缩图片
// file 为从 input file 控件中选择的文件对象
// options:
// {
// quality: 80 压缩质量,数值越大质量越高,反之则越小
// size: 640 压缩后的图片大小,按长边等比例缩放至 size 指定的大小
// cb: function(dataURL) {
// // 压缩后的回调函数
// // dataURL 为jpg数据的base64字符串
// }
// }
function androidCompress(file, options) {
var reader = new FileReader();
reader.onloadend = function(e) {
// reader 对象
var host = e.target;
// 读取的文件内容
var data = host.result;
// 错误对象
var error= host.error;
// 错误消息
var errorMsg;
// 二进制字节数组
var rawBinary;
// 内存图片
var memImg;
// 图片 mime type
var mimeType;
if (error != null) {
switch (error.code) {
case error.NOT_FOUND_ERR:
errorMsg = 'File not found!';
break;
case error.SECURITY_ERR:
errorMsg = 'Security issue with file!';
break;
case error.NOT_READABLE_ERR:
errorMsg = 'File could not be read!';
break;
case error.ENCODING_ERR:
errorMsg = 'Encoding error!';
break;
default:
errorMsg = 'Unknown issue!';
};
throw new Error(errorMsg);
} else {
// 检测文件类型必须为图片
rawBinary = blobutil.base64ToArrayBuffer(data);
mimeType = imagemime(rawBinary);
if (mimeType == null) {
throw new Error('请选择图片');
}
// 如果没有返回 mime type ,则自动补
if (!/^data:image\/.+?;base64,/i.test(data)) {
data = 'data:' + mimeType + ';base64,' + data.split(',')[1];
}
// 载入内存图片,根据配置压缩图片
memImg = new Image();
memImg.onload = function() {
// 获取选择的图片的方向信息
var tags = EXIF.readFromBinaryFile(rawBinary.buffer);
var orientation = tags.Orientation || 1;
// 配置参数
var opts = Object.create(options || {});
var optsQuality = opts.quality || 80;
var optsSize = opts.size || 640;
var optsCb = opts.cb;
// 原始图尺寸
var iw = this.naturalWidth || this.width;
var ih = this.naturalHeight || this.height;
console.log('原始图片信息:', iw, ih)
// 原始图长边大小
var lss = iw > ih ? iw : ih;
// 压缩图尺寸
var cw;
var ch;
// 画布
var canvas;
var ctx;
var dataURL;
if (optsSize < lss) {
// 指定的大小比原图小,计算长边的缩放比例,从而计算出短边的大小
if (iw > ih) {
cw = optsSize;
// 向左移动 0 位的作用是转换为整数
ch = (ih * cw / iw) << 0;
} else {
ch = optsSize;
// 向左移动 0 位的作用是转换为整数
cw = (iw * ch / ih) << 0;
}
} else {
// 指定的大小比原图大,则使用原图尺寸压缩
cw = iw;
ch = ih;
}
// 根据图片方向信息设置画布宽高,变换画布坐标系
canvas = document.createElement('canvas');
ctx = canvas.getContext('2d');
transformCoordinate(canvas, ctx, cw, ch, orientation);
ctx.drawImage(this, 0, 0, cw, ch);
// 导出压缩后的图片数据
dataURL = canvas.toDataURL('image/jpeg', optsQuality / 100);
// 部分设备可能无法正确导出 jpeg data url
if (!/^data:image\/jpeg;base64,.+$/i.test(dataURL)) {
// 压缩图片为 jpg 格式
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
dataURL = new JPEGEncoder().encode(imageData, optsQuality);
}
if (optsCb) {
optsCb(dataURL);
}
};
memImg.onerror = function() {
throw new Error('不支持' + mimeType + '格式');
};
memImg.src = data;
}
};
reader.readAsDataURL(file);
}
function iosCompress(file, options) {
var reader = new FileReader();
reader.onloadend = function(e) {
// reader 对象
var host = e.target;
// 读取的文件内容
var data = host.result;
// 错误对象
var error= host.error;
// 错误消息
var errorMsg;
// 二进制字节数组
var rawBinary;
// 内存图片
var memImg;
// 图片 mime type
var mimeType;
if (error != null) {
switch (error.code) {
case error.NOT_FOUND_ERR:
errorMsg = 'File not found!';
break;
case error.SECURITY_ERR:
errorMsg = 'Security issue with file!';
break;
case error.NOT_READABLE_ERR:
errorMsg = 'File could not be read!';
break;
case error.ENCODING_ERR:
errorMsg = 'Encoding error!';
break;
default:
errorMsg = 'Unknown issue!';
};
throw new Error(errorMsg);
} else {
// 检测文件类型必须为图片
mimeType = file.type;
if (!/^image\/.+/i.test(mimeType)) {
throw new Error('请选择图片');
}
// 载入内存图片,根据配置压缩图片
memImg = new Image();
memImg.onload = function() {
// 获取选择的图片的方向信息
rawBinary = blobutil.base64ToArrayBuffer(data);
var tags = EXIF.readFromBinaryFile(rawBinary.buffer);
var orientation = tags.Orientation || 1;
// 配置参数
var opts = Object.create(options || {});
var optsQuality = opts.quality || 80;
var optsSize = opts.size || 640;
var optsCb = opts.cb;
// 原始图尺寸
var iw = this.naturalWidth || this.width;
var ih = this.naturalHeight || this.height;
console.log('原始图片信息:', iw, ih)
// 原始图长边大小
var lss = iw > ih ? iw : ih;
// 压缩图尺寸
var cw;
var ch;
var cr;
// 画布
var canvas;
var ctx;
var subsampled;
var d;
var tmpCanvas;
var tmpCtx;
var vertSquashRatio;
var dx;
var dy;
var dw;
var dh;
var sx;
var sy;
var sw;
var sh;
var dataURL;
var imageData;
var jpegURL;
var doSquash = mimeType === 'image/jpeg';
if (optsSize < lss) {
// 指定的大小比原图小,计算长边的缩放比例,从而计算出短边的大小
if (iw > ih) {
cw = optsSize;
// 向左移动 0 位的作用是转换为整数
ch = (ih * cw / iw) << 0;
} else {
ch = optsSize;
// 向左移动 0 位的作用是转换为整数
cw = (iw * ch / ih) << 0;
}
} else {
// 指定的大小比原图大,则使用原图尺寸压缩
cw = iw;
ch = ih;
}
// 根据图片方向信息设置画布宽高,变换画布坐标系
canvas = document.createElement('canvas');
ctx = canvas.getContext('2d');
ctx.save();
transformCoordinate(canvas, ctx, cw, ch, orientation);
// 图片二次抽样处理
subsampled = detectSubsampling(this);
if (subsampled) {
iw /= 2;
ih /= 2;
}
// 超大图片分块处理
// 将一幅大图分成若干个小块绘制到目标画布上
// 每个块的大小为 1024 * 1024
d = 1024;
tmpCanvas = document.createElement('canvas');
tmpCanvas.width = tmpCanvas.height = d;
tmpCtx = tmpCanvas.getContext('2d');
vertSquashRatio = doSquash ? detectVerticalSquash(this, iw, ih) : 1;
sy = 0;
// 分割块循环
while (sy < ih) {
// 计算分割块高
sh = sy + d > ih ? ih - sy : d;
sx = 0;
while (sx < iw) {
// 计算分割块宽
sw = sx + d > iw ? iw - sx : d;
// 清空临时画布上的所有内容
tmpCtx.clearRect(0, 0, d, d);
// 将整个源图绘制到临时画布上的指定位置,通过指定负坐标将不是当前块的内容绘制到临时画布以外,从而
// 画布以内的区域就是当前块
tmpCtx.drawImage(this, -sx, -sy);
// 当前块绘制到目标画布上的 x 轴的指定位置(如果有缩放进行缩放)
dx = Math.floor(sx * cw / iw);
// 当前块绘制到目标画布上的宽(如果有缩放进行缩放)
dw = Math.ceil(sw * cw / iw);
// 当前块绘制到目标画布上的 y 轴的指定位置(如果有缩放进行缩放,同时处理垂直压扁比例)
dy = Math.floor(sy * ch / ih / vertSquashRatio);
// 当前块绘制到目标画布上的高(如果有缩放进行缩放,同时处理垂直压扁比例)
dh = Math.ceil(sh * ch / ih / vertSquashRatio);
// 绘制当前块到目标画布上的指定位置
ctx.drawImage(tmpCanvas, 0, 0, sw, sh, dx, dy, dw, dh);
sx += d;
}
sy += d;
}
ctx.restore();
tmpCanvas = tmpCtx = null;
// 导出压缩后的图片数据
dataURL = canvas.toDataURL('image/jpeg', optsQuality / 100);
if (optsCb) {
optsCb(dataURL);
}
};
memImg.onerror = function() {
throw new Error('不支持' + mimeType + '格式');
};
memImg.src = data;
}
};
reader.readAsDataURL(file);
}
var compress = (function() {
if (ios) {
return iosCompress;
}
return androidCompress;
})();
export default compress
/* eslint-disable */
/*
Basic GUI blocking jpeg encoder ported to JavaScript and optimized by
Andreas Ritter, www.bytestrom.eu, 11/2009.
Example usage is given at the bottom of this file.
---------
Copyright (c) 2008, Adobe Systems Incorporated
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Adobe Systems Incorporated nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
function JPEGEncoder(quality) {
var self = this;
var fround = Math.round;
var ffloor = Math.floor;
var YTable = new Array(64);
var UVTable = new Array(64);
var fdtbl_Y = new Array(64);
var fdtbl_UV = new Array(64);
var YDC_HT;
var UVDC_HT;
var YAC_HT;
var UVAC_HT;
var bitcode = new Array(65535);
var category = new Array(65535);
var outputfDCTQuant = new Array(64);
var DU = new Array(64);
var byteout = [];
var bytenew = 0;
var bytepos = 7;
var YDU = new Array(64);
var UDU = new Array(64);
var VDU = new Array(64);
var clt = new Array(256);
var RGB_YUV_TABLE = new Array(2048);
var currentQuality;
var ZigZag = [
0, 1, 5, 6,14,15,27,28,
2, 4, 7,13,16,26,29,42,
3, 8,12,17,25,30,41,43,
9,11,18,24,31,40,44,53,
10,19,23,32,39,45,52,54,
20,22,33,38,46,51,55,60,
21,34,37,47,50,56,59,61,
35,36,48,49,57,58,62,63
];
var std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0];
var std_dc_luminance_values = [0,1,2,3,4,5,6,7,8,9,10,11];
var std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d];
var std_ac_luminance_values = [
0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,
0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,
0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,
0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,
0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,
0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,
0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,
0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,
0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,
0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,
0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,
0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,
0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,
0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,
0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,
0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,
0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,
0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
0xf9,0xfa
];
var std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0];
var std_dc_chrominance_values = [0,1,2,3,4,5,6,7,8,9,10,11];
var std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77];
var std_ac_chrominance_values = [
0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,
0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,
0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,
0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,
0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,
0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,
0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,
0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,
0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,
0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,
0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,
0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,
0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,
0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,
0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,
0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,
0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,
0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,
0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
0xf9,0xfa
];
function initQuantTables(sf){
var YQT = [
16, 11, 10, 16, 24, 40, 51, 61,
12, 12, 14, 19, 26, 58, 60, 55,
14, 13, 16, 24, 40, 57, 69, 56,
14, 17, 22, 29, 51, 87, 80, 62,
18, 22, 37, 56, 68,109,103, 77,
24, 35, 55, 64, 81,104,113, 92,
49, 64, 78, 87,103,121,120,101,
72, 92, 95, 98,112,100,103, 99
];
for (var i = 0; i < 64; i++) {
var t = ffloor((YQT[i]*sf+50)/100);
if (t < 1) {
t = 1;
} else if (t > 255) {
t = 255;
}
YTable[ZigZag[i]] = t;
}
var UVQT = [
17, 18, 24, 47, 99, 99, 99, 99,
18, 21, 26, 66, 99, 99, 99, 99,
24, 26, 56, 99, 99, 99, 99, 99,
47, 66, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99
];
for (var j = 0; j < 64; j++) {
var u = ffloor((UVQT[j]*sf+50)/100);
if (u < 1) {
u = 1;
} else if (u > 255) {
u = 255;
}
UVTable[ZigZag[j]] = u;
}
var aasf = [
1.0, 1.387039845, 1.306562965, 1.175875602,
1.0, 0.785694958, 0.541196100, 0.275899379
];
var k = 0;
for (var row = 0; row < 8; row++)
{
for (var col = 0; col < 8; col++)
{
fdtbl_Y[k] = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0));
fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0));
k++;
}
}
}
function computeHuffmanTbl(nrcodes, std_table){
var codevalue = 0;
var pos_in_table = 0;
var HT = new Array();
for (var k = 1; k <= 16; k++) {
for (var j = 1; j <= nrcodes[k]; j++) {
HT[std_table[pos_in_table]] = [];
HT[std_table[pos_in_table]][0] = codevalue;
HT[std_table[pos_in_table]][1] = k;
pos_in_table++;
codevalue++;
}
codevalue*=2;
}
return HT;
}
function initHuffmanTbl()
{
YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values);
UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values);
YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values);
UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values);
}
function initCategoryNumber()
{
var nrlower = 1;
var nrupper = 2;
for (var cat = 1; cat <= 15; cat++) {
//Positive numbers
for (var nr = nrlower; nr<nrupper; nr++) {
category[32767+nr] = cat;
bitcode[32767+nr] = [];
bitcode[32767+nr][1] = cat;
bitcode[32767+nr][0] = nr;
}
//Negative numbers
for (var nrneg =-(nrupper-1); nrneg<=-nrlower; nrneg++) {
category[32767+nrneg] = cat;
bitcode[32767+nrneg] = [];
bitcode[32767+nrneg][1] = cat;
bitcode[32767+nrneg][0] = nrupper-1+nrneg;
}
nrlower <<= 1;
nrupper <<= 1;
}
}
function initRGBYUVTable() {
for(var i = 0; i < 256;i++) {
RGB_YUV_TABLE[i] = 19595 * i;
RGB_YUV_TABLE[(i+ 256)>>0] = 38470 * i;
RGB_YUV_TABLE[(i+ 512)>>0] = 7471 * i + 0x8000;
RGB_YUV_TABLE[(i+ 768)>>0] = -11059 * i;
RGB_YUV_TABLE[(i+1024)>>0] = -21709 * i;
RGB_YUV_TABLE[(i+1280)>>0] = 32768 * i + 0x807FFF;
RGB_YUV_TABLE[(i+1536)>>0] = -27439 * i;
RGB_YUV_TABLE[(i+1792)>>0] = - 5329 * i;
}
}
// IO functions
function writeBits(bs)
{
var value = bs[0];
var posval = bs[1]-1;
while ( posval >= 0 ) {
if (value & (1 << posval) ) {
bytenew |= (1 << bytepos);
}
posval--;
bytepos--;
if (bytepos < 0) {
if (bytenew == 0xFF) {
writeByte(0xFF);
writeByte(0);
}
else {
writeByte(bytenew);
}
bytepos=7;
bytenew=0;
}
}
}
function writeByte(value)
{
byteout.push(clt[value]); // write char directly instead of converting later
}
function writeWord(value)
{
writeByte((value>>8)&0xFF);
writeByte((value )&0xFF);
}
// DCT & quantization core
function fDCTQuant(data, fdtbl)
{
var d0, d1, d2, d3, d4, d5, d6, d7;
/* Pass 1: process rows. */
var dataOff=0;
var i;
const I8 = 8;
const I64 = 64;
for (i=0; i<I8; ++i)
{
d0 = data[dataOff];
d1 = data[dataOff+1];
d2 = data[dataOff+2];
d3 = data[dataOff+3];
d4 = data[dataOff+4];
d5 = data[dataOff+5];
d6 = data[dataOff+6];
d7 = data[dataOff+7];
var tmp0 = d0 + d7;
var tmp7 = d0 - d7;
var tmp1 = d1 + d6;
var tmp6 = d1 - d6;
var tmp2 = d2 + d5;
var tmp5 = d2 - d5;
var tmp3 = d3 + d4;
var tmp4 = d3 - d4;
/* Even part */
var tmp10 = tmp0 + tmp3; /* phase 2 */
var tmp13 = tmp0 - tmp3;
var tmp11 = tmp1 + tmp2;
var tmp12 = tmp1 - tmp2;
data[dataOff] = tmp10 + tmp11; /* phase 3 */
data[dataOff+4] = tmp10 - tmp11;
var z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */
data[dataOff+2] = tmp13 + z1; /* phase 5 */
data[dataOff+6] = tmp13 - z1;
/* Odd part */
tmp10 = tmp4 + tmp5; /* phase 2 */
tmp11 = tmp5 + tmp6;
tmp12 = tmp6 + tmp7;
/* The rotator is modified from fig 4-8 to avoid extra negations. */
var z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */
var z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */
var z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */
var z3 = tmp11 * 0.707106781; /* c4 */
var z11 = tmp7 + z3; /* phase 5 */
var z13 = tmp7 - z3;
data[dataOff+5] = z13 + z2; /* phase 6 */
data[dataOff+3] = z13 - z2;
data[dataOff+1] = z11 + z4;
data[dataOff+7] = z11 - z4;
dataOff += 8; /* advance pointer to next row */
}
/* Pass 2: process columns. */
dataOff = 0;
for (i=0; i<I8; ++i)
{
d0 = data[dataOff];
d1 = data[dataOff + 8];
d2 = data[dataOff + 16];
d3 = data[dataOff + 24];
d4 = data[dataOff + 32];
d5 = data[dataOff + 40];
d6 = data[dataOff + 48];
d7 = data[dataOff + 56];
var tmp0p2 = d0 + d7;
var tmp7p2 = d0 - d7;
var tmp1p2 = d1 + d6;
var tmp6p2 = d1 - d6;
var tmp2p2 = d2 + d5;
var tmp5p2 = d2 - d5;
var tmp3p2 = d3 + d4;
var tmp4p2 = d3 - d4;
/* Even part */
var tmp10p2 = tmp0p2 + tmp3p2; /* phase 2 */
var tmp13p2 = tmp0p2 - tmp3p2;
var tmp11p2 = tmp1p2 + tmp2p2;
var tmp12p2 = tmp1p2 - tmp2p2;
data[dataOff] = tmp10p2 + tmp11p2; /* phase 3 */
data[dataOff+32] = tmp10p2 - tmp11p2;
var z1p2 = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */
data[dataOff+16] = tmp13p2 + z1p2; /* phase 5 */
data[dataOff+48] = tmp13p2 - z1p2;
/* Odd part */
tmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */
tmp11p2 = tmp5p2 + tmp6p2;
tmp12p2 = tmp6p2 + tmp7p2;
/* The rotator is modified from fig 4-8 to avoid extra negations. */
var z5p2 = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */
var z2p2 = 0.541196100 * tmp10p2 + z5p2; /* c2-c6 */
var z4p2 = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */
var z3p2 = tmp11p2 * 0.707106781; /* c4 */
var z11p2 = tmp7p2 + z3p2; /* phase 5 */
var z13p2 = tmp7p2 - z3p2;
data[dataOff+40] = z13p2 + z2p2; /* phase 6 */
data[dataOff+24] = z13p2 - z2p2;
data[dataOff+ 8] = z11p2 + z4p2;
data[dataOff+56] = z11p2 - z4p2;
dataOff++; /* advance pointer to next column */
}
// Quantize/descale the coefficients
var fDCTQuant;
for (i=0; i<I64; ++i)
{
// Apply the quantization and scaling factor & Round to nearest integer
fDCTQuant = data[i]*fdtbl[i];
outputfDCTQuant[i] = (fDCTQuant > 0.0) ? ((fDCTQuant + 0.5)|0) : ((fDCTQuant - 0.5)|0);
//outputfDCTQuant[i] = fround(fDCTQuant);
}
return outputfDCTQuant;
}
function writeAPP0()
{
writeWord(0xFFE0); // marker
writeWord(16); // length
writeByte(0x4A); // J
writeByte(0x46); // F
writeByte(0x49); // I
writeByte(0x46); // F
writeByte(0); // = "JFIF",'\0'
writeByte(1); // versionhi
writeByte(1); // versionlo
writeByte(0); // xyunits
writeWord(1); // xdensity
writeWord(1); // ydensity
writeByte(0); // thumbnwidth
writeByte(0); // thumbnheight
}
function writeSOF0(width, height)
{
writeWord(0xFFC0); // marker
writeWord(17); // length, truecolor YUV JPG
writeByte(8); // precision
writeWord(height);
writeWord(width);
writeByte(3); // nrofcomponents
writeByte(1); // IdY
writeByte(0x11); // HVY
writeByte(0); // QTY
writeByte(2); // IdU
writeByte(0x11); // HVU
writeByte(1); // QTU
writeByte(3); // IdV
writeByte(0x11); // HVV
writeByte(1); // QTV
}
function writeDQT()
{
writeWord(0xFFDB); // marker
writeWord(132); // length
writeByte(0);
for (var i=0; i<64; i++) {
writeByte(YTable[i]);
}
writeByte(1);
for (var j=0; j<64; j++) {
writeByte(UVTable[j]);
}
}
function writeDHT()
{
writeWord(0xFFC4); // marker
writeWord(0x01A2); // length
writeByte(0); // HTYDCinfo
for (var i=0; i<16; i++) {
writeByte(std_dc_luminance_nrcodes[i+1]);
}
for (var j=0; j<=11; j++) {
writeByte(std_dc_luminance_values[j]);
}
writeByte(0x10); // HTYACinfo
for (var k=0; k<16; k++) {
writeByte(std_ac_luminance_nrcodes[k+1]);
}
for (var l=0; l<=161; l++) {
writeByte(std_ac_luminance_values[l]);
}
writeByte(1); // HTUDCinfo
for (var m=0; m<16; m++) {
writeByte(std_dc_chrominance_nrcodes[m+1]);
}
for (var n=0; n<=11; n++) {
writeByte(std_dc_chrominance_values[n]);
}
writeByte(0x11); // HTUACinfo
for (var o=0; o<16; o++) {
writeByte(std_ac_chrominance_nrcodes[o+1]);
}
for (var p=0; p<=161; p++) {
writeByte(std_ac_chrominance_values[p]);
}
}
function writeSOS()
{
writeWord(0xFFDA); // marker
writeWord(12); // length
writeByte(3); // nrofcomponents
writeByte(1); // IdY
writeByte(0); // HTY
writeByte(2); // IdU
writeByte(0x11); // HTU
writeByte(3); // IdV
writeByte(0x11); // HTV
writeByte(0); // Ss
writeByte(0x3f); // Se
writeByte(0); // Bf
}
function processDU(CDU, fdtbl, DC, HTDC, HTAC){
var EOB = HTAC[0x00];
var M16zeroes = HTAC[0xF0];
var pos;
const I16 = 16;
const I63 = 63;
const I64 = 64;
var DU_DCT = fDCTQuant(CDU, fdtbl);
//ZigZag reorder
for (var j=0;j<I64;++j) {
DU[ZigZag[j]]=DU_DCT[j];
}
var Diff = DU[0] - DC; DC = DU[0];
//Encode DC
if (Diff==0) {
writeBits(HTDC[0]); // Diff might be 0
} else {
pos = 32767+Diff;
writeBits(HTDC[category[pos]]);
writeBits(bitcode[pos]);
}
//Encode ACs
var end0pos = 63; // was const... which is crazy
for (; (end0pos>0)&&(DU[end0pos]==0); end0pos--) {};
//end0pos = first element in reverse order !=0
if ( end0pos == 0) {
writeBits(EOB);
return DC;
}
var i = 1;
var lng;
while ( i <= end0pos ) {
var startpos = i;
for (; (DU[i]==0) && (i<=end0pos); ++i) {}
var nrzeroes = i-startpos;
if ( nrzeroes >= I16 ) {
lng = nrzeroes>>4;
for (var nrmarker=1; nrmarker <= lng; ++nrmarker)
writeBits(M16zeroes);
nrzeroes = nrzeroes&0xF;
}
pos = 32767+DU[i];
writeBits(HTAC[(nrzeroes<<4)+category[pos]]);
writeBits(bitcode[pos]);
i++;
}
if ( end0pos != I63 ) {
writeBits(EOB);
}
return DC;
}
function initCharLookupTable(){
var sfcc = String.fromCharCode;
for(var i=0; i < 256; i++){ ///// ACHTUNG // 255
clt[i] = sfcc(i);
}
}
this.encode = function(image,quality,toRaw) // image data object
{
var time_start = new Date().getTime();
if(quality) setQuality(quality);
// Initialize bit writer
byteout = new Array();
bytenew=0;
bytepos=7;
// Add JPEG headers
writeWord(0xFFD8); // SOI
writeAPP0();
writeDQT();
writeSOF0(image.width,image.height);
writeDHT();
writeSOS();
// Encode 8x8 macroblocks
var DCY=0;
var DCU=0;
var DCV=0;
bytenew=0;
bytepos=7;
this.encode.displayName = "_encode_";
var imageData = image.data;
var width = image.width;
var height = image.height;
var quadWidth = width*4;
var tripleWidth = width*3;
var x, y = 0;
var r, g, b;
var start,p, col,row,pos;
while(y < height){
x = 0;
while(x < quadWidth){
start = quadWidth * y + x;
p = start;
col = -1;
row = 0;
for(pos=0; pos < 64; pos++){
row = pos >> 3;// /8
col = ( pos & 7 ) * 4; // %8
p = start + ( row * quadWidth ) + col;
if(y+row >= height){ // padding bottom
p-= (quadWidth*(y+1+row-height));
}
if(x+col >= quadWidth){ // padding right
p-= ((x+col) - quadWidth +4)
}
r = imageData[ p++ ];
g = imageData[ p++ ];
b = imageData[ p++ ];
/* // calculate YUV values dynamically
YDU[pos]=((( 0.29900)*r+( 0.58700)*g+( 0.11400)*b))-128; //-0x80
UDU[pos]=(((-0.16874)*r+(-0.33126)*g+( 0.50000)*b));
VDU[pos]=((( 0.50000)*r+(-0.41869)*g+(-0.08131)*b));
*/
// use lookup table (slightly faster)
YDU[pos] = ((RGB_YUV_TABLE[r] + RGB_YUV_TABLE[(g + 256)>>0] + RGB_YUV_TABLE[(b + 512)>>0]) >> 16)-128;
UDU[pos] = ((RGB_YUV_TABLE[(r + 768)>>0] + RGB_YUV_TABLE[(g + 1024)>>0] + RGB_YUV_TABLE[(b + 1280)>>0]) >> 16)-128;
VDU[pos] = ((RGB_YUV_TABLE[(r + 1280)>>0] + RGB_YUV_TABLE[(g + 1536)>>0] + RGB_YUV_TABLE[(b + 1792)>>0]) >> 16)-128;
}
DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);
DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
x+=32;
}
y+=8;
}
////////////////////////////////////////////////////////////////
// Do the bit alignment of the EOI marker
if ( bytepos >= 0 ) {
var fillbits = [];
fillbits[1] = bytepos+1;
fillbits[0] = (1<<(bytepos+1))-1;
writeBits(fillbits);
}
writeWord(0xFFD9); //EOI
if(toRaw) {
var len = byteout.length;
var data = new Uint8Array(len);
for (var i=0; i<len; i++ ) {
data[i] = byteout[i].charCodeAt();
}
//cleanup
byteout = [];
// benchmarking
var duration = new Date().getTime() - time_start;
console.log('Encoding time: '+ duration + 'ms');
return data;
}
var jpegDataUri = 'data:image/jpeg;base64,' + btoa(byteout.join(''));
byteout = [];
// benchmarking
var duration = new Date().getTime() - time_start;
console.log('Encoding time: '+ duration + 'ms');
return jpegDataUri
}
function setQuality(quality){
if (quality <= 0) {
quality = 1;
}
if (quality > 100) {
quality = 100;
}
if(currentQuality == quality) return // don't recalc if unchanged
var sf = 0;
if (quality < 50) {
sf = Math.floor(5000 / quality);
} else {
sf = Math.floor(200 - quality*2);
}
initQuantTables(sf);
currentQuality = quality;
console.log('Quality set to: '+quality +'%');
}
function init(){
var time_start = new Date().getTime();
if(!quality) quality = 50;
// Create tables
initCharLookupTable()
initHuffmanTbl();
initCategoryNumber();
initRGBYUVTable();
setQuality(quality);
var duration = new Date().getTime() - time_start;
console.log('Initialization '+ duration + 'ms');
}
init();
};
export default JPEGEncoder;
/* Example usage. Quality is an int in the range [0, 100]
function example(quality){
// Pass in an existing image from the page
var theImg = document.getElementById('testimage');
// Use a canvas to extract the raw image data
var cvs = document.createElement('canvas');
cvs.width = theImg.width;
cvs.height = theImg.height;
var ctx = cvs.getContext("2d");
ctx.drawImage(theImg,0,0);
var theImgData = (ctx.getImageData(0, 0, cvs.width, cvs.height));
// Encode the image and get a URI back, toRaw is false by default
var jpegURI = encoder.encode(theImgData, quality);
var img = document.createElement('img');
img.src = jpegURI;
document.body.appendChild(img);
}
Example usage for getting back raw data and transforming it to a blob.
Raw data is useful when trying to send an image over XHR or Websocket,
it uses around 30% less bytes then a Base64 encoded string. It can
also be useful if you want to save the image to disk using a FileWriter.
NOTE: The browser you are using must support Blobs
function example(quality){
// Pass in an existing image from the page
var theImg = document.getElementById('testimage');
// Use a canvas to extract the raw image data
var cvs = document.createElement('canvas');
cvs.width = theImg.width;
cvs.height = theImg.height;
var ctx = cvs.getContext("2d");
ctx.drawImage(theImg,0,0);
var theImgData = (ctx.getImageData(0, 0, cvs.width, cvs.height));
// Encode the image and get a URI back, set toRaw to true
var rawData = encoder.encode(theImgData, quality, true);
blob = new Blob([rawData.buffer], {type: 'image/jpeg'});
var jpegURI = URL.createObjectURL(blob);
var img = document.createElement('img');
img.src = jpegURI;
document.body.appendChild(img);
}*/
### 前端H5基于canvas的图片压缩
#### 用法
```
var compressimg = require('compressimg');
compressimg(file, {
size: 640,
quality: 80,
cb: function(dataurl) {
var image = new Image();
image.src = dataurl;
}
});
```
- file 为从 input file 控件中选择的文件对象
- size 为源图压缩后长边的大小,短边按比例自动计算
- quality 为图片压缩质量
- cb 为压缩图片成功后的回调函数,参数`dataurl`为压缩后的图片的`data:image/jpeg,base64,...`
\ No newline at end of file
import Cookies from 'js-cookie'
const TokenKey = 'token-rnr-h5'
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
import { Toast } from 'vant'
import { base64toFile } from './canvas'
// 蓝牙
export const BLUETOOTH = 'BLUETOOTH'
// 扫码
export const SCAN_CODE = 'SCAN_CODE'
// 调用相册
export const PHOTO = 'PHOTO'
// 相机
export const CAMERA = 'CAMERA'
// 当前是否在app中
export const isInApp = !!window.android
// 调用js-bridge
function JsBridge() {
this.onShowCallback = null
this.onHideCallback = null
this.bluetoothCallback = []
window.callJSAPPStatus = (result) => {
// 如果当前是展示状态,则展示onshow方法
if (result) {
this.onShowCallback && this.onShowCallback()
} else {
this.onHideCallback && this.onHideCallback()
}
}
/**
* 监听报错信息
* @param errorMessage 报错信息
*/
window.callJSErrorInfo = errorMessage => {
errorMessage && Toast(errorMessage.msg)
}
}
/**
* 根据bridge传入的图片base64编码创建指定格式的图片文件
* @param base64 图片的base64编码
*/
function createImage(base64) {
const content = `data:image/jpeg;base64,${base64}`
return {
content,
file: base64toFile(content)
}
}
/**
* 调用相机
* @param callback 回调方法
*/
JsBridge.prototype.getCamera = function(callback) {
// 如果不在app中
if (!isInApp) {
return
}
// 回调函数
window.callJSImages = async(result) => {
try {
// 执行回调
callback(await createImage(result.imageBase64))
// 清空回调函数
window.callJSImages = null
} catch (error) {
console.error(error)
}
}
// 调用bridge方法
window.android.getAndroidCamera()
}
/**
* 调用相册
* @param callback 回调方法
*/
JsBridge.prototype.getPhoto = function(callback) {
// 如果不在app中
if (!isInApp) {
return
}
// 回调函数
window.callJSImages = async(result) => {
// 执行回调
callback(await createImage(result.imageBase64))
// 清空回调函数
window.callJSImages = null
}
// 调用bridge方法
window.android.getAndroidPhoto(1)
}
/**
* 调用相机,扫条形码二维码
* @param isBarcode 是否是条形码
* @param callback 回调方法
*/
JsBridge.prototype.scanCode = function(isBarcode, callback) {
// 如果不在app中
if (!isInApp) {
return
}
// 先创建回调
window.callJSCodeScan = result => {
console.log('扫码结果是:', result)
callback(result)
window.callJSCodeScan = null
}
// 调用bridge方法
window.android.getAndroidCodeScan(true, -1)
}
/**
* 从设备获取数据
* @param callback 回调方法
*/
JsBridge.prototype.getDeviceData = function(callback) {
// 如果不在app中
if (!isInApp) {
return
}
// 设备链接状态
window.callJSBluetooth = (result) => {
// 执行回调
callback(result)
// 清空回调
window.callJSBluetooth = null
}
// 调用bridge方法
window.android.getAndroidBluetooth()
}
/**
* 查看设备链接状态
* @param callback 回调方法
*/
JsBridge.prototype.deviceConnectStatus = function(callback) {
// 如果不在app中
if (!isInApp) {
return
}
// 设备链接状态
window.callJSDeviceStatus = (appstatus) => {
// 执行回调
callback(appstatus)
// 清空回调
window.callJSDeviceStatus = null
}
// 校验设备状态
window.android.getAndroidDeviceStatus()
}
/**
* 页面处于展示状态
* @param callback 回调方法
*/
JsBridge.prototype.onShow = function(callback) {
// 如果不在app中
if (!isInApp) {
return
}
this.onShowCallback = callback
// 默认先执行一次onshow事件
this.onShowCallback()
}
/**
* 页面处于隐藏状态
* @param callback 回调方法
*/
JsBridge.prototype.onHide = function(callback) {
// 如果不在app中
if (!isInApp) {
return
}
// 绑定页面离开方法
this.onHideCallback = callback
}
/**
* 通过NFC读取设备信息
*/
JsBridge.prototype.readIdCardByNFC = function(callback) {
if (!isInApp) {
return
}
// 设备链接状态
window.callJSBluetooth = (result) => {
// 执行回调
callback(result)
// 清空回调
window.callJSBluetooth = null
}
// 调用bridge方法
window.android.readIDCardNFC()
}
/**
* 获取当前nfc设备的设备数据
*/
JsBridge.prototype.readIDCardByOTG = function(callback) {
// 如果不在app中
if (!isInApp) {
return
}
// 设备链接状态
window.callJSBluetooth = (result) => {
// 执行回调
callback(result)
// 清空回调
window.callJSBluetooth = null
}
// 调用bridge方法
window.android.readIDCardByOTG()
}
/**
* 获取当前nfc设备的设备数据
*/
JsBridge.prototype.geOtgReadType = function(callback) {
// 如果不在app中
if (!isInApp) {
return
}
// 调用bridge方法
callback(window.android.geOtgReadType())
}
/**
* 选择蓝牙设备
*/
JsBridge.prototype.chooseBluetoothDevice = function(callback) {
// 如果不在app中
if (!isInApp) {
return
}
// 设备链接状态
window.callJSDeviceStatus = (result) => {
// 执行回调
callback(result)
// 清空回调
window.callJSDeviceStatus = null
}
// 调用bridge方法
window.android.choiceBluetoothDevice()
}
export default new JsBridge()
import Vue from 'vue'
const bus = new Vue()
export default bus
const H5_CACHE_STORAGE_KEY = 'H5_CACHE_STORAGE'
function Cache() {
try {
this.data = JSON.parse(localStorage.getItem(H5_CACHE_STORAGE_KEY)) || {}
this.noStorageData = {}
} catch (error) {
this.data = {}
}
}
/**
* 向缓存中塞值
* @param key 字段key
* @param value 字段值
*/
Cache.prototype.set = function(key, value, config = {}) {
// 将数据在缓存中保存一份
if (config.noStorage) {
this.noStorageData[key] = value
} else {
this.data[key] = value
localStorage.setItem(H5_CACHE_STORAGE_KEY, JSON.stringify(this.data))
}
}
/**
* 从缓存中获取值
* @param key 字段key
* @param defaultKey 没取到值的默认值
*/
Cache.prototype.get = function(key, defaultValue) {
return this.data[key] || this.noStorageData[key] || defaultValue
}
/**
* 将值从缓存中移除
* @param key 字段key
*/
Cache.prototype.remove = function(key) {
// 删除字段
delete this.data[key]
// 将数据在缓存中保存一份
localStorage.setItem(H5_CACHE_STORAGE_KEY, JSON.stringify(this.data))
}
export default new Cache()
// 如果当前没有
if (!HTMLCanvasElement.prototype.toBlob) {
Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
value: function(callback, type, quality) {
var canvas = this
setTimeout(() => {
var binStr = window.atob(canvas.toDataURL(type, quality).split(',')[1])
var len = binStr.length
var arr = new Uint8Array(len)
for (var i = 0; i < len; i++) {
arr[i] = binStr.charCodeAt(i)
}
callback(new Blob([arr], { type: type || 'image/png' }))
})
}
})
}
/**
* 将base64转换成文件
* @param dataurl base64地址
* @param filename 文件名
*/
export const base64toFile = (dataurl, filename = 'file') => {
const arr = dataurl.split(',')
const mime = arr[0].match(/:(.*?);/)[1]
const suffix = mime.split('/')[1]
const bstr = atob(arr[1])
let n = bstr.length
const u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], `${filename}.${suffix}`, { type: mime })
}
export const SEX_OPTIONS = [
{ name: '', value: 1 },
{ name: '', value: 2 }
]
export const CARD_TYPE = [
{ name: '居民身份证', value: 'IDCARD' },
{ name: '户口簿', value: 'HOUSEHOLD' },
{ name: '中国人民解放军军人身份证件', value: 'PLAIDCARD' },
{ name: '中国人民武装警察身份证件', value: 'CAPFIDCARD' },
{ name: '港澳居民来往内地通行证', value: 'HKIDCARD' },
{ name: '台湾居民来往大陆通行证', value: 'TAIBAOZHENG' },
{ name: '外国公民护照', value: 'PASSPORT' },
{ name: '港澳居民居住证', value: 'HKRESIDENCECARD' },
{ name: '台湾居民居住证', value: 'TWRESIDENCECARD' }
]
export const COMPANY_TYPE = [
{ name: '国有企业', value: '0' },
{ name: '集体所有制', value: '1' },
{ name: '私营企业', value: '2' },
{ name: '有限责任公司', value: '3' },
{ name: '股份有限公司', value: '4' },
{ name: '有限合伙企业', value: '5' },
{ name: '联营企业', value: '6' },
{ name: '外商投资企业', value: '7' },
{ name: '个人独资企业', value: '8' },
{ name: '其他', value: '9' }
]
export const INDUSTRY_TYPE = [
{ name: '农、林、牧、渔业', value: '0' },
{ name: '采矿业', value: '1' },
{ name: '制造业', value: '2' },
{ name: '电力、热力、燃气及水生产和供应业', value: '3' },
{ name: '建筑业', value: '4' },
{ name: '批发和零售业', value: '5' },
{ name: '交通运输、仓储和邮政业', value: '6' },
{ name: '住宿和餐饮业', value: '7' },
{ name: '信息传输、软件和信息技术服务业', value: '8' },
{ name: '金融业', value: '9' },
{ name: '房地产业', value: '10' },
{ name: '租赁和商务服务业', value: '11' },
{ name: '科学研究和技术服务业', value: '12' },
{ name: '水利、环境和公共设施管理业', value: '13' },
{ name: '居民服务、修理和其他服务业', value: '14' },
{ name: '教育', value: '15' },
{ name: '卫生和社会工作', value: '16' },
{ name: '文化、体育和娱乐业', value: '17' },
{ name: '公共管理、社会保障和社会组织', value: '18' },
{ name: '国际组织', value: '19' },
{ name: '其他', value: '20' }
]
export const COMPANY_CERT_TYPE = [
{ name: '统一社会信用代码', value: '0' },
{ name: '组织机构代码证', value: '1' },
{ name: '营业执照', value: '2' },
{ name: '事业单位法人证书或者社会团体法人登记证书', value: '3' }
]
import Big from 'big.js'
import dayjs from 'dayjs'
/**
* Created by PanJiaChen on 16/11/18.
*/
/**
* Parse the time to string
* @param {(Object|string|number)} time
* @param {string} cFormat
* @returns {string | null}
*/
export function parseTime(time, cFormat) {
if (arguments.length === 0 || !time) {
return null
}
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string')) {
if ((/^[0-9]+$/.test(time))) {
// support "1548221490638"
time = parseInt(time)
} else {
// support safari
// https://stackoverflow.com/questions/4310953/invalid-date-in-safari
time = time.replace(new RegExp(/-/gm), '/')
}
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
const value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') { return ['', '', '', '', '', '', ''][value ] }
return value.toString().padStart(2, '0')
})
return time_str
}
/**
* @param {number} time
* @param {string} option
* @returns {string}
*/
export function formatTime(time, option) {
if (('' + time).length === 10) {
time = parseInt(time) * 1000
} else {
time = +time
}
const d = new Date(time)
const now = Date.now()
const diff = (now - d) / 1000
if (diff < 30) {
return '刚刚'
} else if (diff < 3600) {
// less 1 hour
return Math.ceil(diff / 60) + '分钟前'
} else if (diff < 3600 * 24) {
return Math.ceil(diff / 3600) + '小时前'
} else if (diff < 3600 * 24 * 2) {
return '1天前'
}
if (option) {
return parseTime(time, option)
} else {
return (
d.getMonth() +
1 +
'' +
d.getDate() +
'' +
d.getHours() +
'' +
d.getMinutes() +
''
)
}
}
/**
* @param {string} url
* @returns {Object}
*/
export function param2Obj(url) {
const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
if (!search) {
return {}
}
const obj = {}
const searchArr = search.split('&')
searchArr.forEach(v => {
const index = v.indexOf('=')
if (index !== -1) {
const name = v.substring(0, index)
const val = v.substring(index + 1, v.length)
obj[name] = val
}
})
return obj
}
/**
* 检查是否为数字
* @param {any} str 待检查数据
* @return {boolean} true表示是有效的数字;false表示不是有效的数字
*/
export function isNum(str) {
const checkStr = String(str)
if (checkStr === '') {
return false
}
const reg = /^[-+]?\d*(?:\.\d+)?$/i
return reg.test(checkStr)
}
/**
* 格式化数字为金额格式
* @param {string|number} num 数字
* @param {string} thousandSep 千分隔符
* @param {string} fixed 保留的小数位数
* @param {number} roundMode 舍入模式
* @return {any} 格式化后的数字
*/
export function formatNum(num, thousandSep = ',', fixed = 2, roundMode = Big.roundDown) {
if (!isNum(num)) {
return num
}
const str = String(num)
const firstChar = str[0]
const sign = (firstChar === '+' || firstChar === '-' ? firstChar : '')
const handleStr = sign ? str.substring(1) : str
const bigInst = new Big(handleStr)
const fixedStr = bigInst.toFixed(fixed, roundMode)
const arr = fixedStr.split('.')
const zs = arr[0]
const xs = arr[1] || ''
const reg = /(\d)(?=(\d{3})+$)/g
const zsFmt = zs.replace(reg, `$1${thousandSep}`)
const xsFmt = xs ? `.${xs}` : ''
const completeFmt = `${sign}${zsFmt}${xsFmt}`
return completeFmt
}
/**
* 触发浏览器下载文件
* @param {Blob} blob 文件对象
* @param {string} fileName 文件名
*/
export function saveBlob(blob, fileName) {
const a = document.createElement('a')
a.style = 'display:none;'
document.body.appendChild(a)
const fileUrl = window.URL.createObjectURL(blob)
a.href = fileUrl
a.download = fileName
a.click()
window.URL.revokeObjectURL(fileUrl)
document.body.removeChild(a)
}
/**
* 解析日期字条串为日期对象
* @param {string} str 日期字符串
* @param {string} fmt 日期格式
* @return {obj} 日期对象
*/
export function parseDate(str, fmt) {
return dayjs(str).format(fmt)
}
/**
* 检查日期范围在指定的天数内
* @param {string} beginDate 开始日期字符串
* @param {string} beginDateFmt 开始日期格式字符串
* @param {string} endDate 结束日期字符串
* @param {string} endDateFmt 结束日期格式字符串
* @param {number} max 两个日期相隔的最大天数
* @return {boolean} true两个日期相隔的天数小于等于max;false两个日期相隔的天数大于max
*/
export function checkTimeRange(beginDate, beginDateFmt, endDate, endDateFmt, max) {
const start = parseDate(beginDate, beginDateFmt)
const stop = parseDate(endDate, endDateFmt)
const days = (stop.getTime() - start.getTime()) / (24 * 60 * 60 * 1000)
return days <= max
}
/**
* 检查值是否不为空
* @param {*} val
* @return {boolean} true不为空;false为空
*/
export function isNotBlank(val) {
return val !== null && val !== undefined && val !== '' && !isNaN(val)
}
/**
* 将数组按照字典项排序
* @param target 需要排序的项目
*/
export function dictSort(target) {
return target.sort((a, b) => {
return a.localeCompare(b, 'zh-CN', { numeric: true })
})
}
export default class NiceScale {
constructor(min, max, maxTicks = 10) {
this.min = min
this.max = max
this.maxTicks = maxTicks
this.niceMin = 0
this.niceMax = 0
this.tickSpacing = 0
this.calculate()
}
calculate() {
const range = this.niceNum(this.max - this.min, false)
const tickSpacing = this.niceNum(range / (this.maxTicks - 1), true)
this.niceMin = Math.floor(this.min / tickSpacing) * tickSpacing
this.niceMax = Math.ceil(this.max / tickSpacing) * tickSpacing
this.tickSpacing = tickSpacing
}
niceNum(x, round) {
// exponent of x
const exponent = Math.floor(Math.log10(x))
// fractional part of x
const fraction = x / Math.pow(10, exponent)
// nice, rounded fraction
let niceFraction
if (round) {
if (fraction < 1.5) {
niceFraction = 1
} else if (fraction < 3) {
niceFraction = 2
} else if (fraction < 7) {
niceFraction = 5
} else {
niceFraction = 10
}
} else {
if (fraction <= 1) {
niceFraction = 1
} else if (fraction <= 2) {
niceFraction = 2
} else if (fraction <= 5) {
niceFraction = 5
} else {
niceFraction = 10
}
}
return niceFraction * Math.pow(10, exponent)
}
}
window.os = (() => {
const ua = navigator.userAgent
const isWindowsPhone = /(?:Windows Phone)/.test(ua)
const isSymbian = /(?:SymbianOS)/.test(ua) || isWindowsPhone
const isAndroid = /(?:Android)/.test(ua)
const isFireFox = /(?:Firefox)/.test(ua)
const isChrome = /(?:Chrome|CriOS)/.test(ua)
const isTablet = /(?:iPad|PlayBook)/.test(ua) || (isAndroid && !/(?:Mobile)/.test(ua)) || (isFireFox && /(?:Tablet)/.test(ua))
const isPhone = /(?:iPhone)/.test(ua) && !isTablet
const isPc = !isPhone && !isAndroid && !isSymbian
let isWx = false
try {
isWx = ua.toLowerCase().match(/MicroMessenger/i)[0] === 'micromessenger'
} catch (error) {
isWx = false
}
return {
isWx,
isTablet,
isPhone,
isAndroid,
isPc,
isChrome
}
})()
export const pickerOptions = {
shortcuts: [
{
text: '最近一天',
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24)
picker.$emit('pick', [start, end])
}
},
{
text: '最近三天',
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 3)
picker.$emit('pick', [start, end])
}
},
{
text: '最近一周',
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
picker.$emit('pick', [start, end])
}
}
]
}
import axios from 'axios'
import axiosRetry from 'axios-retry'
import store from '../store'
import contentDisposition from 'content-disposition'
import { Toast } from 'vant'
import { removeToken, getToken } from '@/utils/auth' // get token from cookie
import { saveBlob } from '@/utils/index'
axiosRetry(axios, {
retries: 3,
retryDelay: (retryCount) => 500 * retryCount,
retryCondition: (error) => error.message === 'Network Error' || error.code === 'ECONNABORTED' || false
})
const ajax = function(options) {
const NODE_ENV = process.env.NODE_ENV
const BASE_API = process.env.VUE_APP_BASE_API
const token = getToken()
const defaults = {
responseType: 'json',
method: 'post',
baseURL: BASE_API,
url: '',
data: {},
timeout: 30000,
withCredentials: true,
hideToast: false,
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/json;charset=utf-8',
'Authorization': `bearer ${token}`
},
transformResponse: [
function(data) {
try {
return JSON.parse(data)
} catch (error) {
return data
}
}
]
}
const clone = (target) => {
if (Object.prototype.toString.call(target) === '[object Object]' || Object.prototype.toString.call(target) === '[object Array]') {
const result = Array.isArray(target) ? [] : {}
for (const key in target) {
result[key] = clone(target[key])
}
return result
// 过滤入参前后空格
} else if (typeof target === 'string') {
return target.replace(/(^\s*)|(\s*$)/g, '')
} else {
return target
}
}
options = { ...defaults, ...options }
options.data = options.data ? clone(options.data) : {}
return new Promise((resole, reject) => {
let data = {}
let code = 0
let message = ''
axios(options)
.then((response) => {
const json = response.data || {}
data = json.data || {}
code = json.code
message = json.msg || '接口异常,请稍后再试.'
// 文件下载
if (options.responseType === 'blob') {
const disposition = response.headers['content-disposition']
let fileName = 'default'
let cdResult = null
try {
cdResult = contentDisposition.parse(disposition)
} catch (e) {
console.log(e)
}
if (cdResult && cdResult.parameters && cdResult.parameters.filename) {
fileName = decodeURI(cdResult.parameters.filename)
}
const fileType = response.headers['content-type']
const fileData = new Blob([json], { type: fileType })
saveBlob(fileData, fileName)
resole(json)
return false
}
if (json.success) {
resole(data)
} else if (code === 1101 || code === 1102 || code === 1103 || code === 1104) {
// 1101 token无效或已过期
// 1102 已在其他终端登录 强制下线
// 1103 用户冻结 强制下线
// 1104 权限变更等原因 强制下线
removeToken()
// 获取当前登录人的租户号
const { type } = store.getters.loginUser
// 获取当前是否是C端用户
let isCustomer = type === 'C'
// 如果缓存中没有值,则判断接口
if (!type) {
isCustomer = options.url.indexOf(process.env.VUE_APP_BASIC_DATA_API_C) >= 0
}
// 跳转到登录页
window.location.href = `${process.env.VUE_APP_ROUTER}login/${isCustomer ? 'user' : 'business'}?redirect=${encodeURIComponent(window.location.href)}`
} else {
reject({ code, message })
// hideToast=true或用户信息失效不展示报错信息
if (options.hideToast || options.url.indexOf('user/eiam/user/getCurrUserInfo') >= 0) {
return
}
Toast({ duration: 3500, message })
}
})
.catch((error) => {
if (NODE_ENV !== 'production') {
console.log(error.message)
}
if (error.response) {
code = error.response.status
message = error.response.statusText || error.response.data && error.response.data.msg
} else if (error.request && error.message === 'Network Error') {
// 网络异常
message = '连接似乎有问题,请检查网络.'
} else if (error.request && error.code === 'ECONNABORTED') {
// 请求超时
message = '接口异常,请稍后再试.'
} else if (error.__CANCEL__) {
// 取消请求
message = 'cancel request'
code = '_CANCEL_REQUEST_'
} else {
message = 'params setting error'
}
reject({ code, message })
if (options.hideToast) {
return
}
Toast.fail(message)
})
})
}
export default ajax
import qs from 'qs'
/**
* 获取url中的参数
* @param url 当前需要获取参数的url
*/
export const getUrlParams = url => {
// 已经替换的url和参数
const replaceParams = url.split('?')[1]
return qs.parse(replaceParams)
}
/**
* 改变url中的参数
* @param params 需要变更的参数
*/
export const replaceUrlParams = url => {
// 已经替换的url和参数
const replaceUrl = url.split('?')[0]
// 将参数格式化成json对象
const formatReplaceParams = getUrlParams(url)
// 返回格式化方法
return params => {
Object.keys(params).forEach(key => {
formatReplaceParams[key] = params[key]
})
// 返回替换后的对象
return `${replaceUrl}?${qs.stringify(formatReplaceParams)}`
}
}
/**
* 如果当前是必填项
* @param value 当前输入的值
*/
const isRequired = value => {
if (!value && value !== 0) {
return '请输入内容'
}
}
/**
* 如果当前是身份证号
* @param value 当前输入的值
*/
export const isIdcard = value => {
if (`${value}`.length !== 18) {
return '请输入正确的身份证号码'
}
}
/**
* 如果当前是VIN码
* @param value 当前输入的值
*/
export const isVin = value => {
if (`${value}`.length !== 17) {
return '请输入正确的VIN码'
}
}
export const isIccid = value => {
if (`${value}`.length !== 20) {
return '请输入正确的ICCID'
}
}
/**
* 是否是身份证号
* @param value 当前输入的值
*/
const isIdcardPic = value => {
if (!value || value.length === 0 || value.length !== value.filter(v => !!v).length) {
return '请上传证件照片'
}
// 如果上传照片小于2张
if (value.length < 2) {
return '请至少上传2张证件照片'
}
}
/**
* 当前是否是照片
* @param value 当前输入的值
*/
const isEmptyPics = value => {
if (!value || value.length === 0) {
return '请上传文件'
}
}
/**
* 当前是否是正确的手机号
* @param value 当前输入的值
*/
const isPhone = value => {
if (!`${value}`.match(/^1\d{10}$/)) {
return '请输入正确的手机号'
}
}
// 等待校验的规则
const rules = {
certType: [isRequired],
fullName: [isRequired],
gender: [isRequired],
certAddress: [isRequired],
certExpirationDate: [isRequired],
certNumber: [isRequired],
certPic: [isIdcardPic],
contractPic: [isEmptyPics],
purchaseContractPic: [isEmptyPics],
transferCertificatePic: [isEmptyPics],
authorizationLetterPic: [isEmptyPics],
vin: [isRequired, isVin],
iccid: [isRequired, isIccid],
phone: [isRequired, isPhone],
companyName: [isRequired],
companyCertNumber: [isRequired],
companyCertAddress: [isRequired],
companyContactAddress: [isRequired]
}
/**
* 校验用户输入
* @param 校验参数
*/
export function validate(values, ruleKeys = ['fullName', 'gender', 'certType', 'certNumber', 'certAddress', 'certExpirationDate']) {
// 校验位置的索引
let index = 0
// 校验结果
let result = true
// 校验失败字段
let failField = ''
// 错误描述
let failMessage = ''
// 如果当前没有遍历完全
while (index < ruleKeys.length && result) {
// 当前需要校验的key
const ruleKey = ruleKeys[index]
// 如果当前配置了规则
if (rules[ruleKey]) {
rules[ruleKey].forEach(valid => {
// 获取当前校验结果
failMessage = failMessage || valid(values[ruleKey])
// 如果当前校验失败
result = result && !failMessage
})
// 如果当前校验失败
if (!result) {
failField = ruleKey
}
}
// 索引累加
index++
}
return [failField, failMessage]
}
<template>
<div class="wscn-http404-container">
<div class="wscn-http404">
<div class="pic-404">
<img :src="parentImg" class="pic-404__parent" alt="404">
<img :src="cloudImg" class="pic-404__child left" alt="404">
<img :src="cloudImg" class="pic-404__child mid" alt="404">
<img :src="cloudImg" class="pic-404__child right" alt="404">
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Page404',
data() {
return {
home: process.env.VUE_APP_STATIC,
parentImg: require('@/assets/404_images/404.png'),
cloudImg: require('@/assets/404_images/404_cloud.png')
}
},
computed: {
message() {
return 'The webmaster said that you can not enter this page...'
}
}
}
</script>
<style lang="scss" scoped>
.wscn-http404-container{
transform: translate(-50%,-50%);
position: absolute;
top: 40%;
left: 50%;
}
.wscn-http404 {
position: relative;
width: 750px;
padding: 0 50px;
overflow: hidden;
.pic-404 {
position: relative;
width: 750px;
overflow: hidden;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
width: 750px;
padding: 30px;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&__headline {
font-size: 24px;
line-height: 24px;
color: #222;
font-weight: bold;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 18px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
}
}
</style>
<template>
<div class="bind-info">
<div class="bind-text1">您正在对VIN码为{{ params.vin }}的车</div>
<div v-if="type === 'rebind'">
<div class="bind-text2">进行ICCID卡绑定操作</div>
<img :src="rebindImg" alt="" class="bind-img">
<div class="bind-text3">绑定后将恢复相应的流量服务请输入验证码进行确认</div>
</div>
<div v-if="type === 'unbound'">
<div class="bind-text2">进行解绑操作</div>
<img :src="unbindImg" alt="" class="bind-img">
<div class="bind-text1 mb12">解绑如下卡号:{{ params.iccid }}</div>
<div class="bind-text3">解绑后该车将无法享受我们提供的联网服务,请谨慎操作,并确认您对该车的所属权。</div>
</div>
</div>
</template>
<script>
export default {
name: 'BindInfo',
props: {
type: {
type: String,
default: ''
},
info: {
type: Object,
default: () => ({})
},
params: {
type: Object,
default: () => ({})
}
},
data() {
return {
rebindImg: require('@/assets/images/rebind.png'),
unbindImg: require('@/assets/images/unbound.png')
}
}
}
</script>
<style lang="scss" scoped>
.bind-info {
padding-top: 70px;
.bind-text1 {
font-size: 32px;
color: #242424;
text-align: center;
line-height: 45px;
font-weight: 400;
}
.bind-text2 {
font-size: 32px;
color: #242424;
text-align: center;
line-height: 45px;
font-weight: 600;
}
.bind-text3 {
width: 638px;
font-size: 20px;
color: #242424;
text-align: center;
font-weight: 400;
opacity: 0.4;
margin: 0 auto 40px;
}
.bind-img {
display: block;
width: 648px;
height: 349px;
margin: 84px auto 20px;
}
.mb12 {
margin-bottom: 12px;
}
}
</style>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment