仿微信上传头像,实现拍摄、相册选择、手动缩放、裁剪、蒙版、撤回、还原、上传微信本地文件功能
目前功能基于wx-cropper进行开发,wx-cropper 是一个基于微信小程序的图片裁剪工具
项目地址:gitcode地址
一、触发入口标签
<t-cell hover arrow class="userCell" catchtap="handleChangeHeadImg"><view slot="title" class="userTitle">头像</view><image wx:if="{{userInfo.headImage}}" class="authHead" src="{{userInfo.headImage}}" mode="widthFix" slot="note"/><image wx:else class="authHead" src="../../../images/customCenter/authHead.png" mode="widthFix" slot="note"/></t-cell>
handleChangeHeadImg(){const that = this;wx.chooseImage({count: 1, // 默认9sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有success(res) {const src = res.tempFilePaths[0];// 获取裁剪图片资源后,给data添加src属性及其值wx.navigateTo({url: `/components/cropper/cropper?src=${src}`,})}})
}
二、新增cropper组件
1、cropper.js
// components/cropper/cropper.jsimport WeCropper from './we-cropper.js'const app = getApp()const device = wx.getSystemInfoSync()
console.log(device);
const width = device.windowWidth
const height = device.windowHeight - 50
Component({/*** 组件的属性列表*/properties: {},/*** 组件的初始数据*/data: {cropperOpt: {id: 'cropper',targetId: 'targetCropper',pixelRatio: device.pixelRatio,width,height,scale: 2.5,zoom: 8,cut: {x: (width - 300) / 2,y: (height - 300) / 2,width: 300,height: 300},boundStyle: {color: "#04b00f",mask: 'rgba(0,0,0,0.8)',lineWidth: 1}},/**是否还原 */isReduction: false},/*** 组件的方法列表*/methods: {onLoad(options) {if (options.src) {this.data.src = options.src;}this.init();},touchStart(e) {// this.cropper.touchStart(e)const isReduction = this.data.isReduction;this.cropper.touchStart({touches: e.touches.filter(i => i.x !== undefined)});if (!isReduction) {this.setData({isReduction: true})}},touchMove(e) {// this.cropper.touchMove(e)this.cropper.touchMove({touches: e.touches.filter(i => i.x !== undefined)})},touchEnd(e) {this.cropper.touchEnd(e)},getCropperImage() {let self = this;this.cropper.getCropperImage().then((src) => {console.log('src',src)wx.redirectTo({url: `/customCenter/pages/userInfo/index?src=${src}`,})// wx.previewImage({// urls: [src]// })}).catch(() => {console.log('获取图片地址失败,请稍后重试')})},/**初始化画布 */init() {const {cropperOpt} = this.data,self = this,src = this.data.src;cropperOpt.boundStyle.color = "#04b00f";this.setData({cropperOpt})this.cropper = new WeCropper(cropperOpt).on('ready', (ctx) => {if (src) {ctx.pushOrign(src);}console.log(`wecropper is ready for work!`)}).on('beforeImageLoad', (ctx) => {wx.showToast({title: '上传中',icon: 'loading',duration: 20000})}).on('imageLoad', (ctx) => {wx.hideToast()})},/**还原画布 */reduction() {const isReduction = this.data.isReduction,self = this;if (!isReduction) return;this.cropper.reduction().then(() => {self.setData({isReduction: !isReduction})});},/**旋转画布 */rotate() {this.cropper.rotateAngle = this.cropper.rotateAngle + 1;this.cropper.rotate();},/**取消编辑 */cancel() {wx.navigateBack();}}
})
2、cropper.wxml
<!--components/cropper/cropper.wxml-->
<view class="cropper-wrapper"><canvas class="cropper" disable-scroll="true" bindtouchstart="touchStart" bindtouchmove="touchMove" bindtouchend="touchEnd" style="width:{{cropperOpt.width}}px;height:{{cropperOpt.height}}px;background-color: rgba(0, 0, 0, 0.8)" canvas-id="{{cropperOpt.id}}"><cover-image src='/images/rotate.png' class='rotate' catchtap='rotate'></cover-image><view class='refreshBtn {{!isReduction?"disable":""}}' catchtap='reduction'>还原</view></canvas><canvas class="cropper" disable-scroll="true" style="position: fixed; top: -{{cropperOpt.width * cropperOpt.pixelRatio}}px; left: -{{cropperOpt.height * cropperOpt.pixelRatio}}px; width:{{cropperOpt.width * cropperOpt.pixelRatio}}px;height:{{cropperOpt.height * cropperOpt.pixelRatio}}px;"canvas-id="{{cropperOpt.targetId}}"></canvas><view class="cropper-buttons"><view class='btn' catchtap='cancel'>取消</view><view class='btn' catchtap='getCropperImage'>完成</view></view>
</view>
3、cropper.wxss
/* components/cropper/cropper.wxss */.cropper-wrapper {position: relative;display: flex;flex-direction: row;align-items: center;justify-content: center;height: 100%;
}.cropper-buttons {display: flex;justify-content: space-between;align-items: center;position: absolute;bottom: 0;left: 0;z-index: 99999;width: 100%;height: 120rpx;/* height: 50px; */box-sizing: border-box;font-size: 32rpx;color: white;text-align: center;border-top: 1rpx solid #313131;background: #000000;padding: 0 40rpx;
}/* 分割线 */.cropper {position: absolute;top: 0;left: 0;
}.refreshBtn {position: fixed;right: 40rpx;bottom:160rpx;color: #fff;
}
.cropper-buttons .btn{color: #fff;
}
.disable.refreshBtn{color:rgba(255,255,255,.3);
}.rotate{position: fixed;left: 40rpx;bottom: 160rpx;width: 80rpx;height: 80rpx;
}
4、we-cropper.js
/*** we-cropper v1.3.4* (c) 2019 dlhandsome* @license MIT*/
(function (global, factory) {typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :typeof define === 'function' && define.amd ? define(factory) :(global.WeCropper = factory());
}(this, (function () {'use strict';var device = void 0;var TOUCH_STATE = ['touchstarted', 'touchmoved', 'touchended'];function firstLetterUpper(str) {return str.charAt(0).toUpperCase() + str.slice(1)}function setTouchState(instance) {var arg = [],len = arguments.length - 1;while (len-- > 0) arg[len] = arguments[len + 1];TOUCH_STATE.forEach(function (key, i) {if (arg[i] !== undefined) {instance[key] = arg[i];}});}function validator(instance, o) {Object.defineProperties(instance, o);}function getDevice() {if (!device) {device = wx.getSystemInfoSync();}return device}var tmp = {};var ref = getDevice();var pixelRatio = ref.pixelRatio;var DEFAULT = {id: {default: 'cropper',get: function get() {return tmp.id},set: function set(value) {if (typeof (value) !== 'string') {console.error(("id:" + value + " is invalid"));}tmp.id = value;}},width: {default: 750,get: function get() {return tmp.width},set: function set(value) {if (typeof (value) !== 'number') {console.error(("width:" + value + " is invalid"));}tmp.width = value;}},height: {default: 750,get: function get() {return tmp.height},set: function set(value) {if (typeof (value) !== 'number') {console.error(("height:" + value + " is invalid"));}tmp.height = value;}},pixelRatio: {default: pixelRatio,get: function get() {return tmp.pixelRatio},set: function set(value) {if (typeof (value) !== 'number') {console.error(("pixelRatio:" + value + " is invalid"));}tmp.pixelRatio = value;}},scale: {default: 2.5,get: function get() {return tmp.scale},set: function set(value) {if (typeof (value) !== 'number') {console.error(("scale:" + value + " is invalid"));}tmp.scale = value;}},zoom: {default: 5,get: function get() {return tmp.zoom},set: function set(value) {if (typeof (value) !== 'number') {console.error(("zoom:" + value + " is invalid"));} else if (value < 0 || value > 10) {console.error("zoom should be ranged in 0 ~ 10");}tmp.zoom = value;}},src: {default: '',get: function get() {return tmp.src},set: function set(value) {if (typeof (value) !== 'string') {console.error(("src:" + value + " is invalid"));}tmp.src = value;}},cut: {default: {},get: function get() {return tmp.cut},set: function set(value) {if (typeof (value) !== 'object') {console.error(("cut:" + value + " is invalid"));}tmp.cut = value;}},boundStyle: {default: {},get: function get() {return tmp.boundStyle},set: function set(value) {if (typeof (value) !== 'object') {console.error(("boundStyle:" + value + " is invalid"));}tmp.boundStyle = value;}},onReady: {default: null,get: function get() {return tmp.ready},set: function set(value) {tmp.ready = value;}},onBeforeImageLoad: {default: null,get: function get() {return tmp.beforeImageLoad},set: function set(value) {tmp.beforeImageLoad = value;}},onImageLoad: {default: null,get: function get() {return tmp.imageLoad},set: function set(value) {tmp.imageLoad = value;}},onBeforeDraw: {default: null,get: function get() {return tmp.beforeDraw},set: function set(value) {tmp.beforeDraw = value;}}};var ref$1 = getDevice();var windowWidth = ref$1.windowWidth;function prepare() {var self = this;// v1.4.0 版本中将不再自动绑定we-cropper实例self.attachPage = function () {var pages = getCurrentPages();// 获取到当前page上下文var pageContext = pages[pages.length - 1];// 把this依附在Page上下文的wecropper属性上,便于在page钩子函数中访问Object.defineProperty(pageContext, 'wecropper', {get: function get() {console.warn('Instance will not be automatically bound to the page after v1.4.0\n\n' +'Please use a custom instance name instead\n\n' +'Example: \n' +'this.mycropper = new WeCropper(options)\n\n' +'// ...\n' +'this.mycropper.getCropperImage()');return self}});};self.createCtx = function () {var id = self.id;var targetId = self.targetId;if (id) {self.ctx = self.ctx || wx.createCanvasContext(id);self.targetCtx = self.targetCtx || wx.createCanvasContext(targetId);} else {console.error("constructor: create canvas context failed, 'id' must be valuable");}};self.deviceRadio = windowWidth / 750;}var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};function createCommonjsModule(fn, module) {return module = {exports: {}}, fn(module, module.exports), module.exports;}var tools = createCommonjsModule(function (module, exports) {/*** String type check*/exports.isStr = function (v) {return typeof v === 'string';};/*** Number type check*/exports.isNum = function (v) {return typeof v === 'number';};/*** Array type check*/exports.isArr = Array.isArray;/*** undefined type check*/exports.isUndef = function (v) {return v === undefined;};exports.isTrue = function (v) {return v === true;};exports.isFalse = function (v) {return v === false;};/*** Function type check*/exports.isFunc = function (v) {return typeof v === 'function';};/*** Quick object check - this is primarily used to tell* Objects from primitive values when we know the value* is a JSON-compliant type.*/exports.isObj = exports.isObject = function (obj) {return obj !== null && typeof obj === 'object'};/*** Strict object type check. Only returns true* for plain JavaScript objects.*/var _toString = Object.prototype.toString;exports.isPlainObject = function (obj) {return _toString.call(obj) === '[object Object]'};/*** Check whether the object has the property.*/var hasOwnProperty = Object.prototype.hasOwnProperty;exports.hasOwn = function (obj, key) {return hasOwnProperty.call(obj, key)};/*** Perform no operation.* Stubbing args to make Flow happy without leaving useless transpiled code* with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/)*/exports.noop = function (a, b, c) { };/*** Check if val is a valid array index.*/exports.isValidArrayIndex = function (val) {var n = parseFloat(String(val));return n >= 0 && Math.floor(n) === n && isFinite(val)};});var tools_7 = tools.isFunc;var tools_10 = tools.isPlainObject;var EVENT_TYPE = ['ready', 'beforeImageLoad', 'beforeDraw', 'imageLoad'];function observer() {var self = this;self.on = function (event, fn) {if (EVENT_TYPE.indexOf(event) > -1) {if (tools_7(fn)) {event === 'ready' ?fn(self) :self[("on" + (firstLetterUpper(event)))] = fn;}} else {console.error(("event: " + event + " is invalid"));}return self};}function wxPromise(fn) {return function (obj) {if (obj === void 0) obj = {};return new Promise(function (resolve, reject) {obj.success = function (res) {resolve(res);};obj.fail = function (err) {reject(err);};fn(obj);})}}function draw(ctx, reserve) {if (reserve === void 0) reserve = false;return new Promise(function (resolve) {ctx.draw(reserve, resolve);})}var getImageInfo = wxPromise(wx.getImageInfo);var canvasToTempFilePath = wxPromise(wx.canvasToTempFilePath);var base64 = createCommonjsModule(function (module, exports) {/*! http://mths.be/base64 v0.1.0 by @mathias | MIT license */(function (root) {// Detect free variables `exports`.var freeExports = 'object' == 'object' && exports;// Detect free variable `module`.var freeModule = 'object' == 'object' && module &&module.exports == freeExports && module;// Detect free variable `global`, from Node.js or Browserified code, and use// it as `root`.var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal;if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {root = freeGlobal;}/*--------------------------------------------------------------------------*/var InvalidCharacterError = function (message) {this.message = message;};InvalidCharacterError.prototype = new Error;InvalidCharacterError.prototype.name = 'InvalidCharacterError';var error = function (message) {// Note: the error messages used throughout this file match those used by// the native `atob`/`btoa` implementation in Chromium.throw new InvalidCharacterError(message);};var TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';// http://whatwg.org/html/common-microsyntaxes.html#space-charactervar REGEX_SPACE_CHARACTERS = /[\t\n\f\r ]/g;// `decode` is designed to be fully compatible with `atob` as described in the// HTML Standard. http://whatwg.org/html/webappapis.html#dom-windowbase64-atob// The optimized base64-decoding algorithm used is based on @atk’s excellent// implementation. https://gist.github.com/atk/1020396var decode = function (input) {input = String(input).replace(REGEX_SPACE_CHARACTERS, '');var length = input.length;if (length % 4 == 0) {input = input.replace(/==?$/, '');length = input.length;}if (length % 4 == 1 ||// http://whatwg.org/C#alphanumeric-ascii-characters/[^+a-zA-Z0-9/]/.test(input)) {error('Invalid character: the string to be decoded is not correctly encoded.');}var bitCounter = 0;var bitStorage;var buffer;var output = '';var position = -1;while (++position < length) {buffer = TABLE.indexOf(input.charAt(position));bitStorage = bitCounter % 4 ? bitStorage * 64 + buffer : buffer;// Unless this is the first of a group of 4 characters…if (bitCounter++ % 4) {// …convert the first 8 bits to a single ASCII character.output += String.fromCharCode(0xFF & bitStorage >> (-2 * bitCounter & 6));}}return output;};// `encode` is designed to be fully compatible with `btoa` as described in the// HTML Standard: http://whatwg.org/html/webappapis.html#dom-windowbase64-btoavar encode = function (input) {input = String(input);if (/[^\0-\xFF]/.test(input)) {// Note: no need to special-case astral symbols here, as surrogates are// matched, and the input is supposed to only contain ASCII anyway.error('The string to be encoded contains characters outside of the ' +'Latin1 range.');}var padding = input.length % 3;var output = '';var position = -1;var a;var b;var c;var buffer;// Make sure any padding is handled outside of the loop.var length = input.length - padding;while (++position < length) {// Read three bytes, i.e. 24 bits.a = input.charCodeAt(position) << 16;b = input.charCodeAt(++position) << 8;c = input.charCodeAt(++position);buffer = a + b + c;// Turn the 24 bits into four chunks of 6 bits each, and append the// matching character for each of them to the output.output += (TABLE.charAt(buffer >> 18 & 0x3F) +TABLE.charAt(buffer >> 12 & 0x3F) +TABLE.charAt(buffer >> 6 & 0x3F) +TABLE.charAt(buffer & 0x3F));}if (padding == 2) {a = input.charCodeAt(position) << 8;b = input.charCodeAt(++position);buffer = a + b;output += (TABLE.charAt(buffer >> 10) +TABLE.charAt((buffer >> 4) & 0x3F) +TABLE.charAt((buffer << 2) & 0x3F) +'=');} else if (padding == 1) {buffer = input.charCodeAt(position);output += (TABLE.charAt(buffer >> 2) +TABLE.charAt((buffer << 4) & 0x3F) +'==');}return output;};var base64 = {'encode': encode,'decode': decode,'version': '0.1.0'};// Some AMD build optimizers, like r.js, check for specific condition patterns// like the following:if (typeof undefined == 'function' &&typeof undefined.amd == 'object' &&undefined.amd) {undefined(function () {return base64;});} else if (freeExports && !freeExports.nodeType) {if (freeModule) { // in Node.js or RingoJS v0.8.0+freeModule.exports = base64;} else { // in Narwhal or RingoJS v0.7.0-for (var key in base64) {base64.hasOwnProperty(key) && (freeExports[key] = base64[key]);}}} else { // in Rhino or a web browserroot.base64 = base64;}}(commonjsGlobal));});function makeURI(strData, type) {return 'data:' + type + ';base64,' + strData}function fixType(type) {type = type.toLowerCase().replace(/jpg/i, 'jpeg');var r = type.match(/png|jpeg|bmp|gif/)[0];return 'image/' + r}function encodeData(data) {var str = '';if (typeof data === 'string') {str = data;} else {for (var i = 0; i < data.length; i++) {str += String.fromCharCode(data[i]);}}return base64.encode(str)}/*** 获取图像区域隐含的像素数据* @param canvasId canvas标识* @param x 将要被提取的图像数据矩形区域的左上角 x 坐标* @param y 将要被提取的图像数据矩形区域的左上角 y 坐标* @param width 将要被提取的图像数据矩形区域的宽度* @param height 将要被提取的图像数据矩形区域的高度* @param done 完成回调*/function getImageData(canvasId, x, y, width, height, done) {wx.canvasGetImageData({canvasId: canvasId,x: x,y: y,width: width,height: height,success: function success(res) {done(res);},fail: function fail(res) {done(null);console.error('canvasGetImageData error: ' + res);}});}/*** 生成bmp格式图片* 按照规则生成图片响应头和响应体* @param oData 用来描述 canvas 区域隐含的像素数据 { data, width, height } = oData* @returns {*} base64字符串*/function genBitmapImage(oData) {//// BITMAPFILEHEADER: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183374(v=vs.85).aspx// BITMAPINFOHEADER: http://msdn.microsoft.com/en-us/library/dd183376.aspx//var biWidth = oData.width;var biHeight = oData.height;var biSizeImage = biWidth * biHeight * 3;var bfSize = biSizeImage + 54; // total header size = 54 bytes//// typedef struct tagBITMAPFILEHEADER {// WORD bfType;// DWORD bfSize;// WORD bfReserved1;// WORD bfReserved2;// DWORD bfOffBits;// } BITMAPFILEHEADER;//var BITMAPFILEHEADER = [// WORD bfType -- The file type signature; must be "BM"0x42, 0x4D,// DWORD bfSize -- The size, in bytes, of the bitmap filebfSize & 0xff, bfSize >> 8 & 0xff, bfSize >> 16 & 0xff, bfSize >> 24 & 0xff,// WORD bfReserved1 -- Reserved; must be zero0, 0,// WORD bfReserved2 -- Reserved; must be zero0, 0,// DWORD bfOffBits -- The offset, in bytes, from the beginning of the BITMAPFILEHEADER structure to the bitmap bits.54, 0, 0, 0];//// typedef struct tagBITMAPINFOHEADER {// DWORD biSize;// LONG biWidth;// LONG biHeight;// WORD biPlanes;// WORD biBitCount;// DWORD biCompression;// DWORD biSizeImage;// LONG biXPelsPerMeter;// LONG biYPelsPerMeter;// DWORD biClrUsed;// DWORD biClrImportant;// } BITMAPINFOHEADER, *PBITMAPINFOHEADER;//var BITMAPINFOHEADER = [// DWORD biSize -- The number of bytes required by the structure40, 0, 0, 0,// LONG biWidth -- The width of the bitmap, in pixelsbiWidth & 0xff, biWidth >> 8 & 0xff, biWidth >> 16 & 0xff, biWidth >> 24 & 0xff,// LONG biHeight -- The height of the bitmap, in pixelsbiHeight & 0xff, biHeight >> 8 & 0xff, biHeight >> 16 & 0xff, biHeight >> 24 & 0xff,// WORD biPlanes -- The number of planes for the target device. This value must be set to 11, 0,// WORD biBitCount -- The number of bits-per-pixel, 24 bits-per-pixel -- the bitmap// has a maximum of 2^24 colors (16777216, Truecolor)24, 0,// DWORD biCompression -- The type of compression, BI_RGB (code 0) -- uncompressed0, 0, 0, 0,// DWORD biSizeImage -- The size, in bytes, of the image. This may be set to zero for BI_RGB bitmapsbiSizeImage & 0xff, biSizeImage >> 8 & 0xff, biSizeImage >> 16 & 0xff, biSizeImage >> 24 & 0xff,// LONG biXPelsPerMeter, unused0, 0, 0, 0,// LONG biYPelsPerMeter, unused0, 0, 0, 0,// DWORD biClrUsed, the number of color indexes of palette, unused0, 0, 0, 0,// DWORD biClrImportant, unused0, 0, 0, 0];var iPadding = (4 - ((biWidth * 3) % 4)) % 4;var aImgData = oData.data;var strPixelData = '';var biWidth4 = biWidth << 2;var y = biHeight;var fromCharCode = String.fromCharCode;do {var iOffsetY = biWidth4 * (y - 1);var strPixelRow = '';for (var x = 0; x < biWidth; x++) {var iOffsetX = x << 2;strPixelRow += fromCharCode(aImgData[iOffsetY + iOffsetX + 2]) +fromCharCode(aImgData[iOffsetY + iOffsetX + 1]) +fromCharCode(aImgData[iOffsetY + iOffsetX]);}for (var c = 0; c < iPadding; c++) {strPixelRow += String.fromCharCode(0);}strPixelData += strPixelRow;} while (--y)var strEncoded = encodeData(BITMAPFILEHEADER.concat(BITMAPINFOHEADER)) + encodeData(strPixelData);return strEncoded}/*** 转换为图片base64* @param canvasId canvas标识* @param x 将要被提取的图像数据矩形区域的左上角 x 坐标* @param y 将要被提取的图像数据矩形区域的左上角 y 坐标* @param width 将要被提取的图像数据矩形区域的宽度* @param height 将要被提取的图像数据矩形区域的高度* @param type 转换图片类型* @param done 完成回调*/function convertToImage(canvasId, x, y, width, height, type, done) {if (done === void 0) done = function () { };if (type === undefined) {type = 'png';}type = fixType(type);if (/bmp/.test(type)) {getImageData(canvasId, x, y, width, height, function (data) {var strData = genBitmapImage(data);tools_7(done) && done(makeURI(strData, 'image/' + type));});} else {console.error('暂不支持生成\'' + type + '\'类型的base64图片');}}var CanvasToBase64 = {convertToImage: convertToImage,// convertToPNG: function (width, height, done) {// return convertToImage(width, height, 'png', done)// },// convertToJPEG: function (width, height, done) {// return convertToImage(width, height, 'jpeg', done)// },// convertToGIF: function (width, height, done) {// return convertToImage(width, height, 'gif', done)// },convertToBMP: function (ref, done) {if (ref === void 0) ref = {};var canvasId = ref.canvasId;var x = ref.x;var y = ref.y;var width = ref.width;var height = ref.height;if (done === void 0) done = function () { };return convertToImage(canvasId, x, y, width, height, 'bmp', done)}};function methods() {var self = this;var boundWidth = self.width; // 裁剪框默认宽度,即整个画布宽度var boundHeight = self.height; // 裁剪框默认高度,即整个画布高度var id = self.id;var targetId = self.targetId;var pixelRatio = self.pixelRatio;var ref = self.cut;var x = ref.x;if (x === void 0) x = 0;var y = ref.y;if (y === void 0) y = 0;var width = ref.width;if (width === void 0) width = boundWidth;var height = ref.height;if (height === void 0) height = boundHeight;self.updateCanvas = function (done) {if (self.croperTarget) {let x, y, z, percent = self.rotateAngle % 4,imgLeft = self.imgLeft,imgTop = self.imgTop;switch (percent) {case 0:x = 0;y = 0;break;case 1:x = -self.width / 2;y = self.height - self.width / 2;break;case 2:x = self.width;y = self.height;break;case 3:x = self.height - self.width / 2;y = self.width / 2;break;}z = percent * 0.5;self.ctx.translate(x, y);self.ctx.rotate(-Math.PI * z);// 画布绘制图片self.ctx.drawImage(self.croperTarget,imgLeft,imgTop,self.scaleWidth,self.scaleHeight);// 恢复设置(恢复的步骤要跟你修改的步骤向反)self.ctx.rotate(Math.PI * z);self.ctx.translate(-x, -y);}tools_7(self.onBeforeDraw) && self.onBeforeDraw(self.ctx, self);self.setBoundStyle(self.boundStyle); // 设置边界样式self.ctx.draw(false, done);return self};self.pushOrign = function (src) {self.src = src;tools_7(self.onBeforeImageLoad) && self.onBeforeImageLoad(self.ctx, self);return getImageInfo({src: src}).then(function (res) {var innerAspectRadio = res.width / res.height;var customAspectRadio = width / height;self.croperTarget = res.path;if (innerAspectRadio < customAspectRadio) { //竖屏self.rectX = x;self.baseWidth = width;self.baseHeight = width / innerAspectRadio;self.rectY = y - Math.abs((height - self.baseHeight) / 2);} else { //横屏self.rectY = y;self.baseWidth = height * innerAspectRadio;self.baseHeight = height;self.rectX = x - Math.abs((width - self.baseWidth) / 2);}self.imgLeft = self.rectX;self.imgTop = self.rectY;self.scaleWidth = self.baseWidth;self.scaleHeight = self.baseHeight;self.update();return new Promise(function (resolve) {self.updateCanvas(resolve);self.initSelf = Object.assign({}, self);})}).then(function () {tools_7(self.onImageLoad) && self.onImageLoad(self.ctx, self);})};self.getCropperBase64 = function (done) {if (done === void 0) done = function () { };CanvasToBase64.convertToBMP({canvasId: id,x: x,y: y,width: width,height: height}, done);};self.getCropperImage = function () {var args = [],len = arguments.length;while (len--) args[len] = arguments[len];var customOptions = args[0];var fn = args[args.length - 1];var canvasOptions = {canvasId: id,x: x,y: y,width: width,height: height};var task = function () {return Promise.resolve();};if (tools_10(customOptions) &&customOptions.original) {// original modetask = function () {self.targetCtx.drawImage(self.croperTarget,self.imgLeft * pixelRatio,self.imgTop * pixelRatio,self.scaleWidth * pixelRatio,self.scaleHeight * pixelRatio);canvasOptions = {canvasId: targetId,x: x * pixelRatio,y: y * pixelRatio,width: width * pixelRatio,height: height * pixelRatio};return draw(self.targetCtx)};}return task().then(function () {if (tools_10(customOptions)) {canvasOptions = Object.assign({}, canvasOptions, customOptions);}return canvasToTempFilePath(canvasOptions)}).then(function (res) {var tempFilePath = res.tempFilePath;tools_7(fn) && fn.call(self, tempFilePath);return tempFilePath}).catch(function () {tools_7(fn) && fn.call(self, null);})};}/*** 获取最新缩放值* @param oldScale 上一次触摸结束后的缩放值* @param oldDistance 上一次触摸结束后的双指距离* @param zoom 缩放系数* @param touch0 第一指touch对象* @param touch1 第二指touch对象* @returns {*}*/var getNewScale = function (oldScale, oldDistance, zoom, touch0, touch1) {var xMove, yMove, newDistance;// 计算二指最新距离xMove = Math.round(touch1.x - touch0.x);yMove = Math.round(touch1.y - touch0.y);newDistance = Math.round(Math.sqrt(xMove * xMove + yMove * yMove));return oldScale + 0.001 * zoom * (newDistance - oldDistance)};function update() {var self = this;if (!self.src) {return}self.__oneTouchStart = function (touch) {self.touchX0 = Math.round(touch.x);self.touchY0 = Math.round(touch.y);};self.__oneTouchMove = function (touch) {var xMove, yMove;// 计算单指移动的距离if (self.touchended) {return self.updateCanvas()}xMove = Math.round(touch.x - self.touchX0);yMove = Math.round(touch.y - self.touchY0);// var imgLeft = Math.round(self.rectX + xMove);// var imgTop = Math.round(self.rectY + yMove);var percent = self.rotateAngle % 4, imgLeft, imgTop, left, right;switch (percent) {case 1:left = -yMove;right = xMove;break;case 2:left = -xMove;right = -yMove;break;case 3:left = yMove;right = -xMove;break;default:left = xMove;right = yMove;}imgLeft = self.rectX + left;imgTop = self.rectY + right;self.outsideBound(imgLeft, imgTop);self.updateCanvas();};self.__twoTouchStart = function (touch0, touch1) {var xMove, yMove, oldDistance;self.touchX1 = Math.round(self.rectX + self.scaleWidth / 2);self.touchY1 = Math.round(self.rectY + self.scaleHeight / 2);// 计算两指距离xMove = Math.round(touch1.x - touch0.x);yMove = Math.round(touch1.y - touch0.y);oldDistance = Math.round(Math.sqrt(xMove * xMove + yMove * yMove));self.oldDistance = oldDistance;};self.__twoTouchMove = function (touch0, touch1) {var oldScale = self.oldScale;var oldDistance = self.oldDistance;var scale = self.scale;var zoom = self.zoom;self.newScale = getNewScale(oldScale, oldDistance, zoom, touch0, touch1);// 设定缩放范围self.newScale <= 1 && (self.newScale = 1);self.newScale >= scale && (self.newScale = scale);self.scaleWidth = Math.round(self.newScale * self.baseWidth);self.scaleHeight = Math.round(self.newScale * self.baseHeight);var imgLeft = Math.round(self.touchX1 - self.scaleWidth / 2);var imgTop = Math.round(self.touchY1 - self.scaleHeight / 2);self.outsideBound(imgLeft, imgTop);self.updateCanvas();};self.__xtouchEnd = function () {self.oldScale = self.newScale;self.rectX = self.imgLeft;self.rectY = self.imgTop;};}var handle = {// 图片手势初始监测touchStart: function touchStart(e) {var self = this;var ref = e.touches;var touch0 = ref[0];var touch1 = ref[1];if (!self.src) {return}setTouchState(self, true, null, null);// 计算第一个触摸点的位置,并参照改点进行缩放self.__oneTouchStart(touch0);// 两指手势触发if (e.touches.length >= 2) {self.__twoTouchStart(touch0, touch1);}},// 图片手势动态缩放touchMove: function touchMove(e) {var self = this;var ref = e.touches;var touch0 = ref[0];var touch1 = ref[1];if (!self.src) {return}setTouchState(self, null, true);// 单指手势时触发if (e.touches.length === 1) {self.__oneTouchMove(touch0);}// 两指手势触发if (e.touches.length >= 2) {self.__twoTouchMove(touch0, touch1);}},touchEnd: function touchEnd(e) {var self = this;if (!self.src) {return}setTouchState(self, false, false, true);self.__xtouchEnd();}};function cut() {var self = this;var boundWidth = self.width; // 裁剪框默认宽度,即整个画布宽度var boundHeight = self.height;// 裁剪框默认高度,即整个画布高度var ref = self.cut;var x = ref.x;if (x === void 0) x = 0;var y = ref.y;if (y === void 0) y = 0;var width = ref.width;if (width === void 0) width = boundWidth;var height = ref.height;if (height === void 0) height = boundHeight;/*** 设置边界* @param imgLeft 图片左上角横坐标值* @param imgTop 图片左上角纵坐标值*/self.outsideBound = function (imgLeft, imgTop) {var present = self.rotateAngle % 2;if (present == 0) {self.imgLeft = imgLeft >= x ?x :self.scaleWidth + imgLeft - x <= width ?x + width - self.scaleWidth :imgLeft;self.imgTop = imgTop >= y ?y :self.scaleHeight + imgTop - y <= height ?y + height - self.scaleHeight :imgTop;}else {self.imgLeft = imgLeft;self.imgTop = imgTop;}// self.imgLeft = imgLeft;// self.imgTop = imgTop;};/*** 设置边界样式* @param color 边界颜色*/self.setBoundStyle = function (ref) {if (ref === void 0) ref = {};var color = ref.color;if (color === void 0) color = '#04b00f';var mask = ref.mask;if (mask === void 0) mask = 'rgba(0, 0, 0, 0.3)';var lineWidth = ref.lineWidth;if (lineWidth === void 0) lineWidth = 1;var boundOption = [{start: {x: x - lineWidth,y: y + 10 - lineWidth},step1: {x: x - lineWidth,y: y - lineWidth},step2: {x: x + 10 - lineWidth,y: y - lineWidth}},{start: {x: x - lineWidth,y: y + height - 10 + lineWidth},step1: {x: x - lineWidth,y: y + height + lineWidth},step2: {x: x + 10 - lineWidth,y: y + height + lineWidth}},{start: {x: x + width - 10 + lineWidth,y: y - lineWidth},step1: {x: x + width + lineWidth,y: y - lineWidth},step2: {x: x + width + lineWidth,y: y + 10 - lineWidth}},{start: {x: x + width + lineWidth,y: y + height - 10 + lineWidth},step1: {x: x + width + lineWidth,y: y + height + lineWidth},step2: {x: x + width - 10 + lineWidth,y: y + height + lineWidth}}];// 绘制半透明层self.ctx.beginPath();self.ctx.setFillStyle(mask);self.ctx.fillRect(0, 0, x, boundHeight);self.ctx.fillRect(x, 0, width, y);self.ctx.fillRect(x, y + height, width, boundHeight - y - height);self.ctx.fillRect(x + width, 0, boundWidth - x - width, boundHeight);self.ctx.fill();boundOption.forEach(function (op) {self.ctx.beginPath();self.ctx.setStrokeStyle(color);self.ctx.setLineWidth(lineWidth);self.ctx.moveTo(op.start.x, op.start.y);self.ctx.lineTo(op.step1.x, op.step1.y);self.ctx.lineTo(op.step2.x, op.step2.y);self.ctx.stroke();});};}/**还原 */function reduction() {var self = this;return new Promise(function (resolve) {Object.assign(self, self.initSelf);self.updateCanvas(resolve);})}/**旋转 */function rotate() {var self = this;return new Promise(function (resolve) {self.updateCanvas(resolve)})}var version = "1.3.4";var WeCropper = function WeCropper(params) {var self = this;var _default = {};validator(self, DEFAULT);Object.keys(DEFAULT).forEach(function (key) {_default[key] = DEFAULT[key].default;});Object.assign(self, _default, params);self.prepare();self.attachPage();self.createCtx();self.observer();self.cutt();self.methods();self.init();self.update();return self};WeCropper.prototype.init = function init() {var self = this;var src = self.src;self.version = version;typeof self.onReady === 'function' && self.onReady(self.ctx, self);if (src) {self.pushOrign(src);} else {self.updateCanvas();}setTouchState(self, false, false, false);self.oldScale = 1;self.newScale = 1;self.rotateAngle = 0;return self};Object.assign(WeCropper.prototype, handle);WeCropper.prototype.prepare = prepare;WeCropper.prototype.observer = observer;WeCropper.prototype.methods = methods;WeCropper.prototype.cutt = cut;WeCropper.prototype.update = update;WeCropper.prototype.reduction = reduction;WeCropper.prototype.rotate = rotate;return WeCropper;})));
5、返回原有页面处理本地图片文件
upLoadHeadImg(avatarUrl){var that = thiswx.uploadFile({url: app.globalData.prodApiUrl+'/file/upload', // 仅为示例,非真实的接口地址filePath: avatarUrl,name: 'file', success (res){const data = JSON.parse(res.data)if(data.code==200){that.setData({'userInfo.headImage':data.data.url})console.log(that.data.userInfo,'userInfo')}else{wx.showToast({title: res,icon: 'none'})}}})},
onLoad(options) {this.upLoadHeadImg(options.src)
}
相关文章:
仿微信上传头像,实现拍摄、相册选择、手动缩放、裁剪、蒙版、撤回、还原、上传微信本地文件功能
目前功能基于wx-cropper进行开发,wx-cropper 是一个基于微信小程序的图片裁剪工具 项目地址:gitcode地址 一、触发入口标签 <t-cell hover arrow class"userCell" catchtap"handleChangeHeadImg"><view slot"title&…...
Python 操作 Excel 插入图表:解锁数据可视化的高效密码
Python 操作 Excel 插入图表:解锁数据可视化的高效密码 在数据分析与展示的领域中,Python 凭借其强大的库支持,成为众多开发者与数据工作者的得力助手。将图表嵌入 Excel 文件,不仅能丰富数据呈现形式,还能让信息传递…...
python实战项目66:抓取考研招生专业信息
python实战项目66:抓取考研招生专业信息 一、流程分析二、完整代码一、流程分析 考研招生专业信息所在网页主页如下图: 在搜索框中输入所需查询的专业 点击“开设院校”,如下图所示: 打开浏览器开发者工具抓包,刷新页面,找到xhr数据包。 首先,在zydws.do数据包中抓…...
Awesome-Embodied-AI: 具身机器人的资源库
💡 你是否在寻找具身人工智能(Embodied AI)领域的研究资源?是否希望有一个系统性的资源集合来加速你的研究?今天给大家推荐一个重磅项目! 🌟 为什么需要这个项目? 具身人工智能是一…...
Java位运算符大全
1. Java 支持的位运算符 Java 提供了 7 种位运算符: 运算符名称描述示例&按位与(AND)两个位都为 1 时,结果才为 15 & 3 → 1|按位或(OR)两个位有一个为 1 时,结果就为 15 | 3 → 7^按…...
Using the NCCL Library: A Practical Guide
文章目录 Using the NCCL Library: A Practical GuidePrerequisitesBasic NCCL ConceptsPractical Demo CodeCompilation and ExecutionKey Steps ExplainedCommon Patterns1. Point-to-Point Communication2. Broadcast3. Using Streams Best Practices Using the NCCL Librar…...
UML 活动图详解之小轿车启动活动图分析
目录 一、UML 活动图概述 二、UML 活动图的构成元素详解 (一)活动 (二)动作状态 (三)活动状态 (四)迁移(转换) (五)初始节点 …...
58常用控件_QTextEdit的使用
目录 代码示例: 获取多行输入框的内容 代码示例:验证输入框的各种信号 QTextEdit 表示多行输入框也是一个富文本 & markdown 编辑器 并且能在内容超出编辑框范围时自动提供滚动条 QTextEdit不仅能表示纯文本,还可以表示html和markdown QPlainTextE…...
uniapp-商城-42-shop 后台管理 分包
在uniapp 的全局文件中,讲了分包 pages.json 页面路由 | uni-app官网 主要是用于小程序的打包。超高两M就不能上传的。 看看官网上是怎么说的。 1 subPackages 分包加载配置,此配置为小程序的分包加载机制。 因小程序有体积和资源加载限制…...
Zookeeper断开连接时分布式锁释放问题的解决方案
Zookeeper断开连接时分布式锁释放问题的解决方案 当Zookeeper客户端与服务器断开连接时,可能会导致分布式锁无法正常释放,这是分布式锁实现中需要重点解决的问题。以下是几种解决方案: 1. 利用Zookeeper临时节点的特性 核心原理࿱…...
Nginx配置文件介绍
Nginx 的配置文件是模块化的,不同的配置文件承担着不同的功能,下面为你详细介绍常见的配置文件及其作用: 这些文件在/etc/nginx/目录下: 1、主配置文件 /etc/nginx/nginx.conf 是 Nginx 的核心配置文件,对全局参数进…...
新闻数据接口开发指南:从多源聚合到NLP摘要生成
随着人工智能(AI)技术的飞速发展,新闻行业也迎来了新的变革。AI不仅能够自动化生成新闻内容,还能通过智能推荐系统为用户提供个性化的新闻体验。万维易源提供的“新闻查询”API接口,结合了最新的AI技术,为开…...
【八股消消乐】发送请求有遇到服务不可用吗?如何解决?
😊你好,我是小航,一个正在变秃、变强的文艺倾年。 🔔本专栏《八股消消乐》旨在记录个人所背的八股文,包括Java/Go开发、Vue开发、系统架构、大模型开发、机器学习、深度学习、力扣算法等相关知识点,期待与你…...
【博通芯片方案】调试指令详解版一(无线)
前言 无线路由器的主流芯片方案包括,博通(BCM)、联发科(MTK)和高通等等。掌握常用的调试指令,有助于产品的测试以及故障排查。本系列文章将介绍博通芯片方案调试指令,欢迎有需要的朋友关注和分享。 无线 wl指令 查无线国家码 指令:wl -i wl1 country 说明:本系列文章…...
【Google Colab】利用unsloth针对医疗数据集进行大语言模型的快速微调(含跑通原代码)
【本文概述】 为了快速跑通,首先忽略算力等问题,使用google colab云端服务器,选择unsloth/DeepSeek-R1-Distill-Llama-8B大语言模型进行微调,微调参数只进行了简单的设置。 在微调的时候,实际说明colab对8B的模型微调…...
基于STM32、HAL库的ADS1255IDBR模数转换器ADC驱动程序设计
一、简介: ADS1255IDBR是德州仪器(TI)生产的一款高精度、低噪声、24位ΔΣ模数转换器(ADC),主要特性包括: 24位无丢失码分辨率 高达23位有效分辨率(ENOB) 数据速率可达30kSPS 低噪声: 2.5μV RMS (20SPS时) 可编程增益放大器(PGA): 1-64V/V 单/差分输入配置 内置自校准和系…...
T检验、F检验及样本容量计算学习总结
目录 〇、碎语一、假设检验1.1 两种错误1.2 z检验和t检验1.3 t检验1.3.1 单样本t检验1.3.2 配对样本t检验1.3.3 独立样本t检验1.4 方差齐性检验1.4 卡方检验二、样本容量的计算2.1 AB测试主要的两种应用场景2.2 绝对量的计算公式2.3 率的计算公式参考资料〇、碎语 听到最多的检…...
PDFMathTranslate:让数学公式在PDF翻译中不再痛苦
在日常的论文阅读、教材翻译中,我们经常会遇到一个极其恼人的问题:PDF里的数学公式翻译错乱。即使用上了各种强大的PDF翻译工具,公式依然可能被拆碎、误解,甚至丢失。针对这个痛点,PDFMathTranslate 应运而生。 本文将…...
Docker(二):docker常用命令
一、帮助命令 1、docker 帮助命令 命令说明docker version / docker -v查看docker的版本信息docker info查看docker详细信息docker --help / docker -h查看docker帮助命令,可以查看到相关的其他命令 二、Docker镜像命令 1、docker pull 从远程仓库docker hub 上拉…...
Missashe考研日记-day28
Missashe考研日记-day28 1 专业课408 学习时间:2h学习内容: 今天先是预习了OS关于虚拟内存管理的内容,然后听了一部分视频课,明天接着学。知识点回顾: 1.传统存储管理方式特征:一次性、驻留性。2.局部性原…...
基于esp32实现键值对存储读写c程序例程
在基于 ESP32 的系统中,我们可以使用 NVS(Non-Volatile Storage,非易失性存储)来实现系统配置参数的掉电存储和读写。NVS 是 ESP32 提供的一种存储机制,允许我们将键值对数据存储在闪存中,即使设备掉电&…...
半导体行业如何开展风险管理?有没有半导体风控案例参考?
近年来,供应链中断事件的频发,成了越来越多半导体人的噩梦: ❗ 地缘冲突引爆“氖气危机”,生产成本激增! ❗ 关税政策反复,被迫调整全球供应链布局! ❗ 自然灾害导致工厂停工,原材…...
使用 malloc 函数模拟开辟一个 3x5 的整型二维数组
在 C 语言中,二维数组是非常常见的数据结构,用于表示矩阵或者表格形式的数据。而在动态内存分配的情况下,我们通常使用 malloc 函数来为数组分配内存。这篇博客将介绍如何通过 malloc 动态分配一个 3x5 的整型二维数组,并且使用下…...
Github 热点项目 rowboat 一句话生成多AI智能体!5分钟搭建企业级智能工作流系统
今日高星项目推荐:rowboat凭借1705总星数成为智能协作工具黑马!亮点速递:①自然语言秒变AI流水线——只需告诉它“帮外卖公司处理配送异常”,立刻生成多角色协作方案;②企业工具库即插即用,Python包HTTP接口…...
Redis05-进阶-主从
零、文章目录 Redis05-进阶-主从 1、搭建主从架构 (1)概述 单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。 (2)集群概况 我们搭建的主从…...
rockermq多线程消费者配置
rockermq多线程消费者配置 增加消费者数量实现消费者consumer多线程消费 或是 task分布式部署,原理一样 都是增加 consumer数量,程序在多线程 处理的地方已经添加过 分布式redisson锁 保证数据在多线程下的唯一性。 配置 task.yml 配置文件 (…...
Spring框架的ObjectProvider用法-笔记
在Spring框架中,ObjectProvider 是一个用于灵活获取Bean实例的接口,它允许开发者以编程方式有条件地或可选地获取Bean,而无需强制依赖注入,避免在Bean不存在时启动失败。 1. ObjectProvider 的核心功能 ObjectProvider 是Spring…...
DigitalOcean推出Valkey托管缓存服务
今天我们激动地宣布推出DigitalOcean的Valkey托管缓存服务,这是我们全新的托管数据库服务,能够无缝替换托管缓存(此前称为托管Redis)。Valkey托管缓存服务在你一直依赖的功能基础上,还提供了增强工具来支持你的开发需求…...
如何通过挖掘需求、SEO优化及流量变现成功出海?探索互联网产品的盈利之道
挖掘需求,优化流量,实现变现:互联网出海产品的成功之路 在当今全球化的数字时代,越来越多的企业和个人选择将业务扩展到国际市场。这一趋势不仅为企业带来了新的增长机会,也为个人提供了通过互联网产品实现盈利的途径…...
ASP.NET图片盗链防护指南
图片盗链(Hotlinking)是指其他网站直接链接到你服务器上的图片资源,这会消耗你的带宽和服务器资源。以下是几种在ASP.NET中防止图片盗链的有效方法: 1. 使用URL重写模块(推荐) 在Web.config中配置URL重写规则: xml <system.webServer> <rewrite> …...
2025-4-25 情绪周期视角复盘(mini)
直接说结论,没有前戏哈,国芳集团这波消费的行情就相当于当时机器人大周期里的DS的一个补涨周期,那么红宝丽就是接替了中毅达的衣钵的趋势穿越龙,趋势穿越龙没有结束,仅仅是主升暂停,高位震荡,后…...
Java求职者面试:从Spring Boot到微服务的技术深度探索
场景:互联网大厂Java求职者面试 角色介绍: 面试官:技术精湛,负责把控面试质量。谢飞机:搞笑的程序员,偶尔能答对问题。 第一轮:基础知识 面试官:谢飞机,你能简要介绍…...
wsl(8) -- 图形界面
1. 前言 记录一些关于wsl2图形界面的事情。 2. x11-apps wsl2默认已支持图形界面,只是我们选择安装的wsl2 ubuntu发行版是非桌面的,其中没有集成桌面应用,Linux的桌面和windows不同,windows的桌面系统是内核的一部分࿰…...
socket套接字-UDP(中)
socket套接字-UDP(上)https://blog.csdn.net/Small_entreprene/article/details/147465441?fromshareblogdetail&sharetypeblogdetail&sharerId147465441&sharereferPC&sharesourceSmall_entreprene&sharefromfrom_link UDP服务器…...
Android源码编译命令详解
一、引言 先看下面几条指令,相信编译过Android源码的人都再熟悉不过的。 source setenv.sh lunch make -j8记得最初刚接触Android时,同事告诉我用上面的指令就可以编译Android源码,指令虽短但过几天就记不全或者忘记顺序,每次编译时还需要看看自己的云笔记,冰冷的指令总…...
AI 发展历史与关键里程碑_附AI 模型清单及典型应用场景以及物流自动化适合的模型选择
AI 发展历史与关键里程碑_附AI 模型清单及典型应用场景以及物流自动化适合的模型选择 下面分三部分进行介绍: 1. AI 发展历史与关键里程碑 1950 年:图灵测试 1950 年,艾伦图灵提出“图灵测试”(Turing Test),首次以可检验的方式讨论机器能否“思考”。# 图灵测试示意:…...
MVCC(多版本并发控制)
MVCC(多版本并发控制)是数据库实现高并发事务的核心技术之一,其核心是通过数据多版本解决读写冲突。以下从技术原理、实现细节、应用场景、优缺点四个方面深入解析。 一、技术原理 1. 核心思想 数据多版本化:每…...
可以隐藏列的表格
今天积累一个可以隐藏列的表格的实现方法 需求: 表格中有一部分列可以隐藏,在列名右侧有一个复选框,点击勾选展示,否则隐藏另有一个小工具栏,其中有每一列对应的复选框,点击可以将隐藏的列再次展示 思路…...
学习MySQL的第十二天
夕阳西下 云霞满天 一、存储过程概述 1.1 理解 含义:存储过程的英文是 Stored Procedure。它的思想很简单,就是一组经过预先编译的SQL语句的封装。 执行过程:存储过程预先存储在MySQL服务器上,需要执行的时候,客户端只需要向服务器端发出调用存储过程的命令,服…...
用Python做有趣的AI项目4:AI 表情识别助手
本项目将使用 计算机视觉 CNN 模型来识别人脸表情,例如: 开心 😊 | 生气 😠 | 悲伤 😢 | 惊讶 😲 | 厌恶 😒 | 害怕 😱 | 中性 😐 🧠 项目目标 实时摄像头…...
2005-2020年 各省-绿色信贷水平原始数据及测算
各省-绿色信贷水平原始数据及测算(2005-2020年).ziphttps://download.csdn.net/download/2401_84585615/90259771 https://download.csdn.net/download/2401_84585615/90259771 绿色信贷是指金融机构向符合环保要求的企业或项目提供的贷款,旨…...
STM32F103_HAL库+寄存器学习笔记21 - CAN接收过滤器:CPU减负神器,提升系统效率的第一道防线
在STM32F103的CAN总线应用中,硬件过滤器(Filter)承担着关键角色。 本章将从寄存器层面深入剖析CAN接收过滤器的工作机制与配置方法,帮助理解如何高效筛选关键信息,减轻CPU负担。 通过合理使用过滤器,不仅能…...
java_基础Java 转义字符学习笔记
Java 转义字符学习笔记 在Java编程中,转义字符用于表示那些无法直接在代码中表示的字符。以下是一些常用的Java转义字符: \t - 制表符:用于实现对齐功能。\n - 换行符:用于在文本中换行。\ - 反斜杠:表示一个反斜杠字…...
JavaScript基础(七)之web APIs
第二部分:Web APIs 目录 第二部分:Web APIs 五、DOM-节点操作 5.1 日期对象 5.1.1 实例化 5.1.2 时间对象方法 5.1.3 时间戳 5.2 节点操作 5.2.1 DOM节点 5.2.2 查找节点 父节点查找: 子节点查找: 兄弟关系查找: 5.2.3 增加节点 创建节点 5.2.4 删除节点 …...
强化学习机器人路径规划——Sparrow复现
强化学习机器人路径规划——Sparrow-v1.1复现教程 Sparrow是一个开源的移动机器人路径规划模拟器,重视模拟速度和轻量化,使用DDQN强化学习方法进行训练。本文在其基础上,增加了绘制训练曲线教程,并给出了自制地图文件,以实现在自己的地图上进行训练。 模型示意图 源码地…...
怎样给MP3音频重命名?是时候管理下电脑中的音频文件名了
在处理大量音频文件时,给这些文件起一个有意义的名字可以帮助我们更高效地管理和查找所需的内容。通过使用专业的文件重命名工具如简鹿文件批量重命名工具,可以极大地简化这一过程。本文将详细介绍如何利用该工具对 MP3 音频文件进行重命名。 步骤一&am…...
【Nova UI】十二、打造组件库之按钮组件(上):迈向功能构建的关键一步
序言 在上一篇文章中,我们深入探索了 icon 组件从测试到全局注册的全过程🎯,成功为其在项目中稳定运行筑牢了根基。此刻,组件库的建设之旅仍在继续,我们将目光聚焦于另一个关键组件 —— 按钮组件。按钮作为用户与界面…...
C++初阶-STL简介
目录 1.什么是STL 2.STL的版本 3.STL的六大组件 4.STL的重要性 4.1在笔试中 4.2在面试中 4.3.在公司中 5.如何学习STL 6.总结和之后的规划 1.什么是STL STL(standard template library-标准模板库);是C标准库的重要组成部分…...
(最短路)洛谷 P6880 JOI2020 奥运公交 题解
题意 给定一个 n n n 点 m m m 边的有向图,每条边从 u u u 指向 v v v,经过这条边的代价为 c c c。点编号为 1 1 1 到 n n n,无自环。 我们可以翻转一条边,即让他从 u u u 指向 v v v 变为从 v v v 指向 u u u&#…...
动态规划算法题1
动态规划做题步骤 确定状态表示:dp表中某一个位置中的值所表示的含义就是状态表示根据状态表示推导状态转移方程:dp[i]等于什么状态转移方程就是什么,用之前或者之后的状态,推导出dp[i]的值初始化(防止越界):根据状态…...