You can use this mapping & function to do this:
function! KeepCursorEsc() abort
let g:search_pos = getcurpos()
return "\<C-C>:call setpos('.', g:search_pos)\<CR>"
cnoremap <expr> <silent> <Esc> KeepCursorEsc()
How it works
We can't use an regular
cmap because we need to run code while the search is still active. Instead we use an expression mapping, which allows us to run a function by evaluating it as an expression.
The function first reads the cursor position with
getcurpos() and then returns a string which is used as the
rhs of the mapping.
rhs cancels the search with
<Esc> used in a
cmap runs the search command instead of cancelling it) and then restores the cursor position to what it was when the mapping was invoked by calling
N.B. Note that the function will also be called when you press Esc while typing a
: command. We should really avoid this by checking the type of the command-line with
getcmdtype(), but for simplicity, this answer does not do so: when you're typing a
: command the cursor will be in the same position anyway, so the function won't move it unexpectedly!